Get the build going.
[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
30 #include <gtk/gtk.h>
31 #include <gdk/gdkkeysyms.h>
32 #include <string.h>
33
34 #include <epan/prefs.h>
35 #include "globals.h"
36 #include "main.h"
37 #include "util.h"
38 #include "gui_utils.h"
39 #include "dlg_utils.h"
40 #include "proto_dlg.h"
41 #include "simple_dialog.h"
42 #include "compat_macros.h"
43 #include "disabled_protos.h"
44 #include <epan/filesystem.h>
45 #include "help_dlg.h"
46
47 static gboolean proto_delete_event_cb(GtkWidget *, GdkEvent *, gpointer);
48 static void proto_ok_cb(GtkWidget *, gpointer);
49 static void proto_apply_cb(GtkWidget *, gpointer);
50 static void proto_save_cb(GtkWidget *, gpointer);
51 static void proto_cancel_cb(GtkWidget *, gpointer);
52 static void proto_destroy_cb(GtkWidget *, gpointer);
53
54 #if GTK_MAJOR_VERSION < 2
55 static void show_proto_selection(GtkCList *proto_list);
56 #else
57 static void show_proto_selection(GtkListStore *proto_store);
58 #endif
59 static gboolean set_proto_selection(GtkWidget *);
60 static gboolean revert_proto_selection(void);
61
62 static void toggle_all_cb(GtkWidget *button, gpointer parent_w);
63 static void enable_all_cb(GtkWidget *button, gpointer parent_w);
64 static void disable_all_cb(GtkWidget *button, gpointer parent_w);
65 #if GTK_MAJOR_VERSION < 2
66 static void proto_list_select_cb(GtkCList *proto_list, gint row, gint col, 
67                                  GdkEventButton *ev, gpointer gp);
68 static gboolean proto_list_keypress_cb(GtkWidget *pl, GdkEventKey *ev,
69                                    gpointer gp);
70 #else
71 static void status_toggled(GtkCellRendererToggle *, gchar *, gpointer);
72 #endif
73
74 static GtkWidget *proto_w = NULL;
75
76 /* list of protocols */
77 static GSList *protocol_list = NULL;
78
79 typedef struct protocol_data {
80   const char  *name;
81   const char  *abbrev;
82   int         hfinfo_index;
83   gboolean    enabled;
84   gboolean    was_enabled;
85 #if GTK_MAJOR_VERSION < 2
86   gint        row;
87 #else
88   GtkTreeIter iter;
89 #endif
90 } protocol_data_t;
91
92 #define DISABLED "Disabled"
93 #define STATUS_TXT(x) ((x) ? "" : DISABLED)
94
95 void
96 proto_cb(GtkWidget *w _U_, gpointer data _U_)
97 {
98
99   GtkWidget *main_vb, *bbox, *proto_list, *label, *proto_sw, *proto_frame,
100             *proto_vb, *button, *ok_bt, *apply_bt, *save_bt, *cancel_bt, *help_bt;
101   const gchar *titles[] = { "Status", "Protocol", "Description" };
102 #if GTK_MAJOR_VERSION < 2
103   gint width;
104 #else
105   GtkListStore *proto_store;
106   GtkCellRenderer *proto_rend;
107   GtkTreeViewColumn *proto_col;
108 #endif
109
110
111   if (proto_w != NULL) {
112     reactivate_window(proto_w);
113     return;
114   }
115
116   proto_w = dlg_window_new("Wireshark: Enabled Protocols");
117   gtk_window_set_default_size(GTK_WINDOW(proto_w), DEF_WIDTH * 2/3, DEF_HEIGHT);
118
119   /* Container for each row of widgets */
120
121   main_vb = gtk_vbox_new(FALSE, 6);
122   gtk_container_border_width(GTK_CONTAINER(main_vb), 6);
123   gtk_container_add(GTK_CONTAINER(proto_w), main_vb);
124   gtk_widget_show(main_vb);
125
126   /* Protocol selection list ("enable/disable" protocols) */
127
128   proto_frame = gtk_frame_new("Enabled Protocols");
129   gtk_box_pack_start(GTK_BOX(main_vb), proto_frame, TRUE, TRUE, 0);
130   gtk_widget_show(proto_frame);
131
132   /* Protocol list */
133   
134   proto_vb = gtk_vbox_new(FALSE, 0);
135   gtk_container_add(GTK_CONTAINER(proto_frame), proto_vb);
136   gtk_container_border_width(GTK_CONTAINER(proto_vb), 5);
137   gtk_widget_show(proto_vb);
138   
139   proto_sw = scrolled_window_new(NULL, NULL);
140 #if GTK_MAJOR_VERSION >= 2
141   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(proto_sw), 
142                                    GTK_SHADOW_IN);
143 #endif
144   gtk_box_pack_start(GTK_BOX(proto_vb), proto_sw, TRUE, TRUE, 0);
145   gtk_widget_show(proto_sw);
146
147 #if GTK_MAJOR_VERSION < 2
148   proto_list = gtk_clist_new_with_titles(3, (gchar **) titles);
149   gtk_container_add(GTK_CONTAINER(proto_sw), proto_list);
150   gtk_clist_set_selection_mode(GTK_CLIST(proto_list), GTK_SELECTION_BROWSE);
151   gtk_clist_column_titles_passive(GTK_CLIST(proto_list));
152   gtk_clist_column_titles_show(GTK_CLIST(proto_list));
153   gtk_clist_set_column_auto_resize(GTK_CLIST(proto_list), 0, FALSE);
154   gtk_clist_set_column_auto_resize(GTK_CLIST(proto_list), 1, TRUE);
155   gtk_clist_set_column_auto_resize(GTK_CLIST(proto_list), 2, TRUE);
156   width = gdk_string_width(proto_list->style->font, DISABLED);
157   gtk_clist_set_column_width(GTK_CLIST(proto_list), 0, width);
158   SIGNAL_CONNECT(proto_list, "select-row", proto_list_select_cb, NULL);
159   SIGNAL_CONNECT(proto_list, "key-press-event", proto_list_keypress_cb, NULL);
160   show_proto_selection(GTK_CLIST(proto_list));
161 #else
162   proto_store = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_STRING,
163                                    G_TYPE_STRING, G_TYPE_POINTER);
164   show_proto_selection(proto_store);
165   /* default sort on "abbrev" column */
166   gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(proto_store), 1,
167                                        GTK_SORT_ASCENDING);
168   proto_list = tree_view_new(GTK_TREE_MODEL(proto_store));
169   gtk_container_add(GTK_CONTAINER(proto_sw), proto_list);
170   proto_rend = gtk_cell_renderer_toggle_new();
171   SIGNAL_CONNECT(proto_rend, "toggled", status_toggled, proto_store);
172   proto_col = gtk_tree_view_column_new_with_attributes(titles[0], proto_rend,
173                                                     "active", 0, NULL);
174   gtk_tree_view_column_set_sort_column_id(proto_col, 0);
175   gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
176   proto_rend = gtk_cell_renderer_text_new();
177   proto_col = gtk_tree_view_column_new_with_attributes(titles[1], proto_rend,
178                                                     "text", 1, NULL);
179   gtk_tree_view_column_set_sort_column_id(proto_col, 1);
180   gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
181   proto_rend = gtk_cell_renderer_text_new();
182   proto_col = gtk_tree_view_column_new_with_attributes(titles[2], proto_rend,
183                                                     "text", 2, NULL);
184   gtk_tree_view_column_set_sort_column_id(proto_col, 2);
185   gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
186   g_object_unref(G_OBJECT(proto_store));
187 #endif
188   gtk_widget_show(proto_list);
189
190   label = gtk_label_new("Disabling a protocol prevents higher "
191                         "layer protocols from being displayed");
192   gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
193   gtk_widget_show(label);
194   gtk_box_pack_start(GTK_BOX(proto_vb), label, FALSE, FALSE, 5);
195
196
197   bbox = gtk_hbutton_box_new();
198   gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
199   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
200   gtk_box_pack_start(GTK_BOX(proto_vb), bbox, FALSE, FALSE, 0);
201   gtk_widget_show(bbox);
202
203   /* Enable All */
204   button = gtk_button_new_with_label("Enable All");
205   SIGNAL_CONNECT(button, "clicked", enable_all_cb, proto_list);
206   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
207   gtk_widget_show(button);
208
209   /* Disable All */
210   button = gtk_button_new_with_label("Disable All");
211   SIGNAL_CONNECT(button, "clicked", disable_all_cb, proto_list);
212   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
213   gtk_widget_show(button);
214
215   /* Invert */
216   button = gtk_button_new_with_label("Invert");
217   SIGNAL_CONNECT(button, "clicked", toggle_all_cb, proto_list);
218   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
219   gtk_widget_show(button);
220
221
222   /* Button row */
223   if(topic_available(HELP_ENABLED_PROTOCOLS_DIALOG)) {
224     bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
225   } else {
226     bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, NULL);
227   }
228   gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
229   gtk_widget_show(bbox);
230
231   ok_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_OK);
232   SIGNAL_CONNECT(ok_bt, "clicked", proto_ok_cb, proto_w);
233   gtk_widget_grab_default(ok_bt);
234
235   apply_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_APPLY);
236   SIGNAL_CONNECT(apply_bt, "clicked", proto_apply_cb, proto_w);
237
238   save_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_SAVE);
239   SIGNAL_CONNECT(save_bt, "clicked", proto_save_cb, proto_w);
240
241   cancel_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
242   window_set_cancel_button(proto_w, cancel_bt, proto_cancel_cb);
243
244   if(topic_available(HELP_ENABLED_PROTOCOLS_DIALOG)) {
245     help_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
246     SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_ENABLED_PROTOCOLS_DIALOG);
247   }
248
249   SIGNAL_CONNECT(proto_w, "delete_event", proto_delete_event_cb, NULL);
250   SIGNAL_CONNECT(proto_w, "destroy", proto_destroy_cb, NULL);
251
252   gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(proto_w));
253
254   gtk_widget_show(proto_w);
255
256   /* hide the Save button if the user uses implicit save */
257   if(!prefs.gui_use_pref_save) {
258     gtk_widget_hide(save_bt);
259   }
260
261   window_present(proto_w);
262 } /* proto_cb */
263
264 #if GTK_MAJOR_VERSION < 2
265 static void
266 proto_list_select_cb(GtkCList *proto_list, gint row, gint col, 
267                      GdkEventButton *ev _U_, gpointer gp _U_) {
268   protocol_data_t *p = gtk_clist_get_row_data(proto_list, row);
269   
270   if (row < 0 || col < 0)
271     return;
272
273   if (p->enabled)
274     p->enabled = FALSE;
275   else
276     p->enabled = TRUE;
277
278   gtk_clist_set_text(proto_list, row, 0, STATUS_TXT(p->enabled) );
279 } /* proto_list_select_cb */
280
281 static gboolean
282 proto_list_keypress_cb(GtkWidget *pl, GdkEventKey *ev, gpointer gp _U_) {
283   GtkCList *proto_list = GTK_CLIST(pl);
284   
285   if (ev->keyval == GDK_space) {
286     proto_list_select_cb(proto_list, proto_list->focus_row, 0, NULL, gp);
287   }
288   return TRUE;
289 }
290
291 #else
292 static void
293 status_toggled(GtkCellRendererToggle *cell _U_, gchar *path_str, gpointer data)
294 {
295   GtkTreeModel    *model = (GtkTreeModel *)data;
296   GtkTreeIter      iter;
297   GtkTreePath     *path = gtk_tree_path_new_from_string(path_str);
298   protocol_data_t *p;
299
300   gtk_tree_model_get_iter(model, &iter, path);
301   gtk_tree_model_get(model, &iter, 3, &p, -1);
302
303   if (p->enabled)
304     p->enabled = FALSE;
305   else
306     p->enabled = TRUE;
307
308   gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, p->enabled, -1);
309
310   gtk_tree_path_free(path);
311 } /* status toggled */
312 #endif
313
314 /* XXX - We need callbacks for Gtk2 */
315
316
317 /* Toggle All */
318 static void
319 toggle_all_cb(GtkWidget *button _U_, gpointer pl)
320 {
321   GSList *entry;
322 #if GTK_MAJOR_VERSION < 2
323   GtkCList *proto_list = GTK_CLIST(pl);
324 #else
325   GtkListStore *s = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(pl)));
326 #endif
327
328   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
329     protocol_data_t *p = entry->data;
330
331     if (p->enabled)
332       p->enabled = FALSE;
333     else
334       p->enabled = TRUE;
335     
336 #if GTK_MAJOR_VERSION < 2
337     gtk_clist_set_text(proto_list, p->row, 0, STATUS_TXT(p->enabled) );
338 #else
339     gtk_list_store_set(s, &p->iter, 0, p->enabled, -1);
340 #endif
341   }
342 }
343
344 /* Enable/Disable All Helper */
345 static void
346 set_active_all(GtkWidget *w, gboolean new_state)
347 {
348
349 #if GTK_MAJOR_VERSION < 2
350   GtkCList *proto_list = GTK_CLIST(w);
351 #else
352   GtkListStore *s = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(w)));
353 #endif
354   GSList *entry;
355
356 #if GTK_MAJOR_VERSION < 2
357   gtk_clist_freeze(proto_list);
358 #endif
359   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
360     protocol_data_t *p = entry->data;
361     
362     p->enabled = new_state;
363 #if GTK_MAJOR_VERSION < 2
364     gtk_clist_set_text(proto_list, p->row, 0, STATUS_TXT(new_state) );
365 #else
366     gtk_list_store_set(s, &p->iter, 0, new_state, -1);
367 #endif
368   }
369 #if GTK_MAJOR_VERSION < 2
370   gtk_clist_thaw(proto_list);
371 #endif
372 }
373
374 /* Enable All */
375 static void
376 enable_all_cb(GtkWidget *button _U_, gpointer pl)
377 {
378         set_active_all(pl, TRUE);
379 }
380
381 /* Disable All */
382 static void
383 disable_all_cb(GtkWidget *button _U_, gpointer pl)
384 {
385         set_active_all(pl, FALSE);
386 }
387
388 static void
389 proto_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
390 {
391   GSList *entry;
392
393   proto_w = NULL;
394
395   /* remove protocol list */
396   if (protocol_list) {
397     for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
398       g_free(entry->data);
399     }
400     g_slist_free(protocol_list);
401     protocol_list = NULL;
402   }
403 }
404
405 /* Treat this as a cancel, by calling "proto_cancel_cb()".
406    XXX - that'll destroy the Protocols dialog; will that upset
407    a higher-level handler that says "OK, we've been asked to delete
408    this, so destroy it"? */
409 static gboolean
410 proto_delete_event_cb(GtkWidget *proto_w, GdkEvent *event _U_,
411                       gpointer dummy _U_)
412 {
413   proto_cancel_cb(NULL, proto_w);
414   return FALSE;
415 }
416
417 static void proto_write(gpointer parent_w _U_)
418 {
419   char *pf_dir_path;
420   char *pf_path;
421   int pf_save_errno;
422
423   /* Create the directory that holds personal configuration files, if
424      necessary.  */
425   if (create_persconffile_dir(&pf_dir_path) == -1) {
426      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
427       "Can't create directory\n\"%s\"\nfor disabled protocols file: %s.", pf_dir_path,
428       strerror(errno));
429      g_free(pf_dir_path);
430   } else {
431     save_disabled_protos_list(&pf_path, &pf_save_errno);
432     if (pf_path != NULL) {
433         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
434             "Could not save to your disabled protocols file\n\"%s\": %s.",
435             pf_path, strerror(pf_save_errno));
436         g_free(pf_path);
437     }
438   }
439 }
440
441 static void
442 proto_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
443 {
444   gboolean redissect;
445
446   /* update the selection now, so we'll save the right things */
447   redissect = set_proto_selection(GTK_WIDGET(parent_w));
448
449   /* if we don't have a Save button, just save the settings now */
450   if (!prefs.gui_use_pref_save) {
451       proto_write(parent_w);
452   }
453
454   window_destroy(GTK_WIDGET(parent_w));
455   if (redissect)
456     cf_redissect_packets(&cfile);
457 }
458
459 static void
460 proto_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
461 {
462   gboolean redissect;
463
464   /* update the selection now, so we'll save the right things */
465   redissect = set_proto_selection(GTK_WIDGET(parent_w));
466
467   /* if we don't have a Save button, just save the settings now */
468   if (!prefs.gui_use_pref_save) {
469       proto_write(parent_w);
470   }
471
472   if (redissect)
473     cf_redissect_packets(&cfile);
474 }
475
476 static void
477 proto_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
478 {
479
480   proto_write(parent_w);
481
482   if (set_proto_selection(GTK_WIDGET(parent_w))) {
483     /* Redissect all the packets, and re-evaluate the display filter. */
484     cf_redissect_packets(&cfile);
485   }
486 }
487
488 static void
489 proto_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
490 {
491   gboolean redissect;
492
493   redissect = revert_proto_selection();
494   window_destroy(GTK_WIDGET(parent_w));
495   if (redissect)
496     cf_redissect_packets(&cfile);
497 }
498
499 static gboolean
500 set_proto_selection(GtkWidget *parent_w _U_)
501 {
502   GSList *entry;
503   gboolean need_redissect = FALSE;
504
505   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
506     protocol_data_t *p = entry->data;
507     protocol_t *protocol;
508
509     protocol = find_protocol_by_id(p->hfinfo_index);
510     if (proto_is_protocol_enabled(protocol) != p->enabled) {
511       proto_set_decoding(p->hfinfo_index, p->enabled);
512       need_redissect = TRUE;
513     }
514   }
515
516   return need_redissect;
517
518 } /* set_proto_selection */
519
520 static gboolean
521 revert_proto_selection(void)
522 {
523   GSList *entry;
524   gboolean need_redissect = FALSE;
525
526   /*
527    * Undo all the changes we've made to protocol enable flags.
528    */
529   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
530     protocol_data_t *p = entry->data;
531     protocol_t *protocol;
532
533     protocol = find_protocol_by_id(p->hfinfo_index);
534     if (proto_is_protocol_enabled(protocol) != p->was_enabled) {
535       proto_set_decoding(p->hfinfo_index, p->was_enabled);
536       need_redissect = TRUE;
537     }
538   }
539
540   return need_redissect;
541
542 } /* revert_proto_selection */
543
544 static gint
545 protocol_data_compare(gconstpointer a, gconstpointer b)
546 {
547   const protocol_data_t *ap = (const protocol_data_t *)a;
548   const protocol_data_t *bp = (const protocol_data_t *)b;
549
550   return strcmp(ap->abbrev, bp->abbrev);
551 }
552
553 static void
554 #if GTK_MAJOR_VERSION < 2
555 show_proto_selection(GtkCList *proto_list)
556 #else
557 show_proto_selection(GtkListStore *proto_store)
558 #endif
559 {
560   GSList *entry;
561   gint i;
562   void *cookie;
563   protocol_t *protocol;
564   protocol_data_t *p;
565 #if GTK_MAJOR_VERSION < 2
566   const gchar *proto_text[3];
567 #endif
568
569   /* Iterate over all the protocols */
570
571   for (i = proto_get_first_protocol(&cookie); i != -1;
572        i = proto_get_next_protocol(&cookie)) {
573       if (proto_can_toggle_protocol(i)) {
574         p = g_malloc(sizeof(protocol_data_t));
575         protocol = find_protocol_by_id(i);
576         p->name = proto_get_protocol_name(i);
577         p->abbrev = proto_get_protocol_short_name(protocol);
578         p->hfinfo_index = i;
579         p->enabled = proto_is_protocol_enabled(protocol);
580         p->was_enabled = p->enabled;
581         protocol_list = g_slist_insert_sorted(protocol_list,
582                                             p, protocol_data_compare);
583       }
584   }
585
586   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
587     p = entry->data;
588
589 #if GTK_MAJOR_VERSION < 2
590     /* XXX - The preferred way to do this would be to have a check box
591      * in the first column.  GtkClists don't let us put arbitrary widgets
592      * in a cell, so we use the word "Disabled" instead.  We should be
593      * able to use check boxes in Gtk2, however.
594      */        
595     proto_text[0] = STATUS_TXT (p->enabled);
596     proto_text[1] = p->abbrev;
597     proto_text[2] = p->name;
598     p->row = gtk_clist_append(proto_list, (gchar **) proto_text);
599     gtk_clist_set_row_data(proto_list, p->row, p);
600 #else
601     gtk_list_store_append(proto_store, &p->iter);
602     gtk_list_store_set(proto_store, &p->iter,
603                        0, p->enabled,
604                        1, p->abbrev,
605                        2, p->name,
606                        3, p,
607                       -1);
608 #endif
609   }
610
611 } /* show_proto_selection */