merge_all_tap_menus() has been moved to menus.c.
[obnox/wireshark/wip.git] / gtk / dlg_utils.c
1 /* dlg_utils.c
2  * Utilities to use when constructing dialogs
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
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 #include <string.h>
29 #include <stdarg.h>
30
31 #include <gtk/gtk.h>
32
33 #include "gtk/gtkglobals.h"
34 #include "gtk/gui_utils.h"
35 #include "gtk/dlg_utils.h"
36 #include "gtk/stock_icons.h"
37
38 #include "epan/filesystem.h"
39
40
41 static void
42 dlg_activate (GtkWidget *widget, gpointer ok_button);
43
44 /* create a button for the button row (helper for dlg_button_row_new) */
45 static GtkWidget *
46 dlg_button_new(GtkWidget *hbox, GtkWidget *button_hbox, const gchar *stock_id)
47 {
48     GtkWidget *button;
49
50     button = gtk_button_new_from_stock(stock_id);
51 #if GTK_CHECK_VERSION(2,18,0)
52     gtk_widget_set_can_default(button, TRUE);
53 #else
54     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
55 #endif
56     g_object_set_data(G_OBJECT(hbox), stock_id, button);
57     gtk_box_pack_end(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
58     gtk_widget_show(button);
59     return button;
60 }
61
62 /*
63  * Set the focus and default for the nth item in a button row, with
64  * 0 being the first item.
65  */
66 #define BUTTON_HBOX_KEY "button_hbox"
67 void
68 dlg_button_focus_nth(GtkWidget *hbox, gint focus_item) {
69     GtkWidget *button_hbox, *button;
70     GList *children;
71     gint cur_item = 0;
72
73     if (!hbox)
74         return;
75
76     button_hbox = g_object_get_data(G_OBJECT(hbox), BUTTON_HBOX_KEY);
77     children = gtk_container_get_children(GTK_CONTAINER(button_hbox));
78
79     while (children) {
80         if (cur_item == focus_item) {
81             button = children->data;
82             gtk_widget_grab_focus(button);
83             gtk_widget_grab_default(button);
84             break;
85         }
86         children = g_list_next(children);
87         cur_item++;
88     }
89
90     g_list_free(children);
91 }
92
93 /* create a button row for a dialog */
94
95 /* The purpose of this is, to have one place available, where all button rows
96  * from all dialogs are laid out. This will:
97  *
98  * a.) keep the button layout more consistent over the different dialogs
99  * b.) being able to switch between different button layouts, e.g.:
100  *     e.g. Win32: "OK" "Apply" "Cancel"
101  *     e.g. GNOME: "Apply" "Cancel" "OK"
102  */
103 GtkWidget *
104 dlg_button_row_new(const gchar *stock_id_first, ...)
105 {
106     gint        buttons = 0;
107     va_list     stock_id_list;
108     const gchar *stock_id = stock_id_first;
109     GtkWidget   *hbox;
110     GtkWidget   *button_hbox;
111     GtkWidget   *help_hbox;
112     GtkWidget   *button;
113
114     const gchar *apply        = NULL;
115     const gchar *cancel       = NULL;
116     const gchar *cap_start    = NULL;
117     const gchar *cap_stop     = NULL;
118     const gchar *cap_options  = NULL;
119 #ifdef _WIN32
120     const gchar *cap_details  = NULL;
121 #endif
122     const gchar *clear        = NULL;
123     const gchar *close        = NULL;
124     const gchar *copy         = NULL;
125     const gchar *create_stat  = NULL;
126     const gchar *delete       = NULL;
127     const gchar *dont_save    = NULL;
128     const gchar *filter_stream= NULL;
129     const gchar *find         = NULL;
130     const gchar *help         = NULL;
131     const gchar *jump         = NULL;
132     const gchar *no           = NULL;
133     const gchar *ok           = NULL;
134     const gchar *print        = NULL;
135     const gchar *save         = NULL;
136     const gchar *save_as      = NULL;
137     const gchar *save_all     = NULL;
138     const gchar *stop         = NULL;
139     const gchar *yes          = NULL;
140 #ifdef HAVE_GEOIP
141     const gchar *map          = NULL;
142 #endif /* HAVE_GEOIP */
143     const gchar *follow_stream = NULL;
144
145
146     va_start(stock_id_list, stock_id_first);
147
148     /* get all buttons needed */
149     while(stock_id != NULL) {
150         if (strcmp(stock_id, GTK_STOCK_OK) == 0) {
151             ok = stock_id;
152         } else if (strcmp(stock_id, WIRESHARK_STOCK_CREATE_STAT) == 0) {
153             create_stat = stock_id;
154         } else if (strcmp(stock_id, GTK_STOCK_APPLY) == 0) {
155             apply = stock_id;
156         } else if (strcmp(stock_id, GTK_STOCK_SAVE) == 0) {
157             save = stock_id;
158         } else if (strcmp(stock_id, GTK_STOCK_SAVE_AS) == 0) {
159             save_as = stock_id;
160         } else if (strcmp(stock_id, WIRESHARK_STOCK_SAVE_ALL) == 0) {
161             save_all = stock_id;
162         } else if (strcmp(stock_id, WIRESHARK_STOCK_DONT_SAVE) == 0) {
163             dont_save = stock_id;
164         } else if (strcmp(stock_id, WIRESHARK_STOCK_QUIT_DONT_SAVE) == 0) {
165             dont_save = stock_id;
166         } else if (strcmp(stock_id, GTK_STOCK_CANCEL) == 0) {
167             cancel = stock_id;
168         } else if (strcmp(stock_id, GTK_STOCK_CLOSE) == 0) {
169             close = stock_id;
170         } else if (strcmp(stock_id, GTK_STOCK_CLEAR) == 0) {
171             clear = stock_id;
172 #ifdef HAVE_LIBPCAP
173         } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_START) == 0) {
174             cap_start = stock_id;
175         } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_STOP) == 0) {
176             cap_stop = stock_id;
177         } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_OPTIONS) == 0) {
178             cap_options = stock_id;
179 #ifdef _WIN32
180         } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_DETAILS) == 0) {
181             cap_details = stock_id;
182 #endif
183 #endif /* HAVE_LIBPCAP */
184 #ifdef HAVE_GEOIP
185         } else if (strcmp(stock_id, WIRESHARK_STOCK_MAP) == 0) {
186             map = stock_id;
187 #endif /* HAVE_GEOIP */
188         } else if (strcmp(stock_id, WIRESHARK_STOCK_FOLLOW_STREAM) == 0) {
189             follow_stream = stock_id;
190         } else if (strcmp(stock_id, GTK_STOCK_STOP) == 0) {
191             stop = stock_id;
192         } else if (strcmp(stock_id, GTK_STOCK_HELP) == 0) {
193             help = stock_id;
194         } else if (strcmp(stock_id, GTK_STOCK_PRINT) == 0) {
195             print = stock_id;
196         } else if (strcmp(stock_id, GTK_STOCK_FIND) == 0) {
197             find = stock_id;
198         } else if (strcmp(stock_id, GTK_STOCK_JUMP_TO) == 0) {
199             jump = stock_id;
200         } else if (strcmp(stock_id, GTK_STOCK_YES) == 0) {
201             yes = stock_id;
202         } else if (strcmp(stock_id, GTK_STOCK_NO) == 0) {
203             no = stock_id;
204         } else if (strcmp(stock_id, WIRESHARK_STOCK_FILTER_OUT_STREAM) == 0) {
205             filter_stream = stock_id;
206         } else if (strcmp(stock_id, GTK_STOCK_DELETE) == 0) {
207             delete = stock_id;
208         } else if (strcmp(stock_id, GTK_STOCK_COPY) == 0) {
209             copy = stock_id;
210         } else {
211             /* we don't know that button! */
212             g_assert_not_reached();
213         }
214         buttons++;
215         stock_id = va_arg(stock_id_list, gchar *);
216     }
217     va_end(stock_id_list);
218
219     hbox = gtk_hbox_new(FALSE, 0);
220     gtk_widget_show(hbox);
221
222     button_hbox = gtk_hbutton_box_new();
223     gtk_box_pack_end(GTK_BOX(hbox), button_hbox, TRUE, TRUE, 0);
224     g_object_set_data(G_OBJECT(hbox), BUTTON_HBOX_KEY, button_hbox);
225     gtk_widget_show(button_hbox);
226     gtk_box_set_spacing(GTK_BOX(button_hbox), 5);
227
228     help_hbox = gtk_hbutton_box_new();
229     gtk_box_pack_end(GTK_BOX(hbox), help_hbox, FALSE, FALSE, 0);
230     gtk_widget_show(help_hbox);
231     gtk_box_set_spacing(GTK_BOX(help_hbox), 5);
232
233     if (buttons == 0) {
234         /* if no buttons wanted, simply do nothing */
235         return hbox;
236     }
237
238     if (buttons == 1) {
239         /* if only one button, simply put it in the middle (default) */
240         dlg_button_new(hbox, button_hbox, stock_id_first);
241         return hbox;
242     }
243
244     /* do we have a help button? -> special handling for it */
245     if (help) {
246         button = gtk_button_new_from_stock(help);
247 #if GTK_CHECK_VERSION(2,18,0)
248         gtk_widget_set_can_default(button, TRUE);
249 #else
250         GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
251 #endif
252         g_object_set_data(G_OBJECT(hbox), help, button);
253         gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
254         gtk_widget_show(button);
255         buttons--;
256     }
257
258     /* do we have a copy button? -> special handling for it */
259     if (copy) {
260         button = gtk_button_new_from_stock(copy);
261 #if GTK_CHECK_VERSION(2,18,0)
262         gtk_widget_set_can_default(button, TRUE);
263 #else
264         GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
265 #endif
266         g_object_set_data(G_OBJECT(hbox), copy, button);
267         gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
268         gtk_widget_show(button);
269         buttons--;
270     }
271
272 #ifdef HAVE_GEOIP
273     /* do we have a map button? -> special handling for it */
274     if (map) {
275         button = gtk_button_new_from_stock(map);
276 #if GTK_CHECK_VERSION(2,18,0)
277         gtk_widget_set_can_default(button, TRUE);
278 #else
279         GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
280 #endif
281         g_object_set_data(G_OBJECT(hbox), map, button);
282         gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
283         gtk_widget_show(button);
284         buttons--;
285     }
286 #endif /* HAVE_GEOIP */
287
288     /* if more than one button, sort buttons from left to right */
289     /* (the whole button cluster will then be right aligned) */
290     gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
291
292 #if !defined(_WIN32)
293     /* beware: sequence of buttons are important! */
294
295     /* XXX: this can be implemented more elegant of course, but it works as it should */
296     if (buttons == 2) {
297         if (ok && cancel) {
298             dlg_button_new(hbox, button_hbox, cancel);
299             dlg_button_new(hbox, button_hbox, ok);
300             return hbox;
301         }
302         if (print && cancel) {
303             dlg_button_new(hbox, button_hbox, cancel);
304             dlg_button_new(hbox, button_hbox, print);
305             return hbox;
306         }
307         if (find && cancel) {
308             dlg_button_new(hbox, button_hbox, cancel);
309             dlg_button_new(hbox, button_hbox, find);
310             return hbox;
311         }
312         if (jump && cancel) {
313             dlg_button_new(hbox, button_hbox, cancel);
314             dlg_button_new(hbox, button_hbox, jump);
315             return hbox;
316         }
317         if (save && cancel) {
318             dlg_button_new(hbox, button_hbox, cancel);
319             dlg_button_new(hbox, button_hbox, save);
320             return hbox;
321         }
322         if (ok && clear) {
323             dlg_button_new(hbox, button_hbox, clear);
324             dlg_button_new(hbox, button_hbox, ok);
325             return hbox;
326         }
327         if (save && close) {
328             dlg_button_new(hbox, button_hbox, close);
329             dlg_button_new(hbox, button_hbox, save);
330             return hbox;
331         }
332         if (create_stat && cancel) {
333             dlg_button_new(hbox, button_hbox, cancel);
334             dlg_button_new(hbox, button_hbox, create_stat);
335             return hbox;
336         }
337         if (cap_start && cancel) {
338             dlg_button_new(hbox, button_hbox, cancel);
339             dlg_button_new(hbox, button_hbox, cap_start);
340             return hbox;
341         }
342         if (cap_stop && cancel) {
343             dlg_button_new(hbox, button_hbox, cancel);
344             dlg_button_new(hbox, button_hbox, cap_stop);
345             return hbox;
346         }
347         if (delete && cancel) {
348             dlg_button_new(hbox, button_hbox, cancel);
349             dlg_button_new(hbox, button_hbox, delete);
350             return hbox;
351         }
352     }
353     if (buttons == 3) {
354         if (ok && save && close) {
355             dlg_button_new(hbox, button_hbox, save);
356             dlg_button_new(hbox, button_hbox, close);
357             dlg_button_new(hbox, button_hbox, ok);
358             return hbox;
359         }
360         if (ok && apply && cancel) {
361             dlg_button_new(hbox, button_hbox, apply);
362             dlg_button_new(hbox, button_hbox, cancel);
363             dlg_button_new(hbox, button_hbox, ok);
364             return hbox;
365         }
366         if (apply && save && close) {
367             dlg_button_new(hbox, button_hbox, save);
368             dlg_button_new(hbox, button_hbox, close);
369             dlg_button_new(hbox, button_hbox, apply);
370             return hbox;
371         }
372         if (yes && no && cancel) {
373             dlg_button_new(hbox, button_hbox, no);
374             dlg_button_new(hbox, button_hbox, cancel);
375             dlg_button_new(hbox, button_hbox, yes);
376             return hbox;
377         }
378         if (save && dont_save && cancel) {
379                 dlg_button_new(hbox, button_hbox, dont_save);
380                 dlg_button_new(hbox, button_hbox, cancel);
381                 dlg_button_new(hbox, button_hbox, save);
382                 return hbox;
383         }
384     }
385     if (buttons == 4) {
386         if (ok && apply && save && cancel) {
387             dlg_button_new(hbox, button_hbox, save);
388             dlg_button_new(hbox, button_hbox, apply);
389             dlg_button_new(hbox, button_hbox, cancel);
390             dlg_button_new(hbox, button_hbox, ok);
391             return hbox;
392         }
393         if (ok && apply && save && close) {
394             dlg_button_new(hbox, button_hbox, save);
395             dlg_button_new(hbox, button_hbox, apply);
396             dlg_button_new(hbox, button_hbox, close);
397             dlg_button_new(hbox, button_hbox, ok);
398             return hbox;
399         }
400     }
401 #endif
402
403     /* beware: sequence of buttons is important! */
404     if (ok      != NULL) dlg_button_new(hbox, button_hbox, ok);
405     if (delete  != NULL) dlg_button_new(hbox, button_hbox, delete);
406     if (jump    != NULL) dlg_button_new(hbox, button_hbox, jump);
407     if (find    != NULL) dlg_button_new(hbox, button_hbox, find);
408     if (print   != NULL) dlg_button_new(hbox, button_hbox, print);
409     if (create_stat != NULL) dlg_button_new(hbox, button_hbox, create_stat);
410     if (apply   != NULL) dlg_button_new(hbox, button_hbox, apply);
411     if (yes     != NULL) dlg_button_new(hbox, button_hbox, yes);
412     if (no      != NULL) dlg_button_new(hbox, button_hbox, no);
413     if (save    != NULL) dlg_button_new(hbox, button_hbox, save);
414     if (save_as    != NULL) dlg_button_new(hbox, button_hbox, save_as);
415     if (save_all    != NULL) dlg_button_new(hbox, button_hbox, save_all);
416     if (dont_save   != NULL) dlg_button_new(hbox, button_hbox, dont_save);
417     if (cap_start   != NULL) dlg_button_new(hbox, button_hbox, cap_start);
418     if (cap_stop    != NULL) dlg_button_new(hbox, button_hbox, cap_stop);
419     if (cap_options != NULL) dlg_button_new(hbox, button_hbox, cap_options);
420 #ifdef _WIN32
421     if (cap_details != NULL) dlg_button_new(hbox, button_hbox, cap_details);
422 #endif
423     if (stop    != NULL) dlg_button_new(hbox, button_hbox, stop);
424     if (clear   != NULL) dlg_button_new(hbox, button_hbox, clear);
425     if (filter_stream!= NULL) dlg_button_new(hbox, button_hbox, filter_stream);
426     if (follow_stream != NULL) dlg_button_new(hbox, button_hbox, follow_stream);
427     if (close   != NULL) dlg_button_new(hbox, button_hbox, close);
428     if (cancel  != NULL) dlg_button_new(hbox, button_hbox, cancel);
429
430     return hbox;
431 }
432
433
434 /* Create a dialog box window that belongs to Wireshark's main window. */
435 GtkWidget *
436 dlg_window_new(const gchar *title)
437 {
438     GtkWidget *win;
439
440     win = window_new(GTK_WINDOW_TOPLEVEL, title);
441
442     /*
443      * XXX - if we're running in the capture child process, we can't easily
444      * make this window transient for the main process's window.  We just
445      * punt here.
446      *
447      * Perhaps the child process should only capture packets, write them to
448      * a file, and somehow notify the parent process and let *it* do all
449      * the GUI work.  If we can do that efficiently (so that we don't drop
450      * more packets), perhaps we can also do so even when we're *not* doing
451      * an "Update list of packets in real time" capture.  That'd let the
452      * child process run set-UID on platforms where you need that in order
453      * to capture, and might also simplify the job of having the GUI main
454      * loop wait both for user input and packet arrival.
455      */
456     /*
457      * On Windows, making the dialogs transient to top_level behaves strangely.
458      * It is not possible any more to bring the top level window to front easily.
459      * So we don't do this on Windows.
460      */
461 #ifndef _WIN32
462     if (top_level) {
463         gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(top_level));
464     }
465 #endif /*_WIN32*/
466
467     return win;
468 }
469
470 /* Create a configuration dialog box window that belongs to Wireshark's
471  * main window and add the name of the current profile name to it's title bar
472  */
473 GtkWidget *
474 dlg_conf_window_new(const gchar *title)
475 {
476     const char *profile_name;
477     gchar      *win_name;
478     GtkWidget  *win;
479
480     /*
481      * Set window title to reflect which preferences profile we are
482      * working with.
483      */
484     profile_name = get_profile_name();
485
486     win_name = g_strdup_printf("%s - Profile: %s", title, profile_name);
487     win = dlg_window_new(win_name);
488
489     g_free(win_name);
490
491     return win;
492 }
493
494 /* Set the "activate" signal for a widget to call a routine to
495    activate the "OK" button for a dialog box.
496
497    XXX - there should be a way to specify that a GtkEntry widget
498    shouldn't itself handle the Return key, but should let it be
499    passed on to the parent, so that you don't have to do this
500    by hand for every GtkEntry widget in a dialog box, but, alas,
501    there isn't.  (Does this problem exist for other widgets?
502    I.e., are there any others that seize the Return key? */
503 void
504 dlg_set_activate(GtkWidget *widget, GtkWidget *ok_button)
505 {
506     g_signal_connect(widget, "activate", G_CALLBACK(dlg_activate), ok_button);
507 }
508
509 static void
510 dlg_activate (GtkWidget *widget _U_, gpointer ok_button)
511 {
512     gtk_widget_activate(GTK_WIDGET(ok_button));
513 }
514