Get rid of a bunch of "Ethereal"s and "ethereal"s in comments, GUI
[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
29 #include <gtk/gtk.h>
30 #include <gdk/gdkkeysyms.h>
31
32 #if 0
33 #include <epan/filesystem.h>
34 #endif
35
36 #if 0
37 #include "globals.h"
38 #endif
39
40 #include "gtkglobals.h"
41 #include "gui_utils.h"
42 #include "dlg_utils.h"
43 #include "compat_macros.h"
44 #if 0
45 #include "main.h"
46 #endif
47
48 #include <string.h>
49 #if 0
50 #include <stdio.h>
51 #endif
52 #include <stdarg.h>
53
54 static void
55 dlg_activate (GtkWidget *widget, gpointer ok_button);
56
57 /* create a button for the button row (helper for dlg_button_row_new) */
58 static GtkWidget *
59 dlg_button_new(GtkWidget *hbox, GtkWidget *button_hbox, const gchar *stock_id)
60 {
61     GtkWidget *button;
62
63     button = BUTTON_NEW_FROM_STOCK(stock_id);
64     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
65     OBJECT_SET_DATA(hbox, stock_id, button);
66     gtk_box_pack_end(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
67     gtk_widget_show(button);
68     return button;
69 }
70
71
72 /* create a button row for a dialog */
73
74 /* The purpose of this is, to have one place available, where all button rows 
75  * from all dialogs are laid out. This will:
76  *
77  * a.) keep the button layout more consistent over the different dialogs
78  * b.) being able to switch between different button layouts, e.g.:
79  *     GTK1 (e.g. win32) "OK" "Apply" "Cancel"
80  *     GTK2 (e.g. GNOME) "Apply" "Cancel" "OK"
81  */
82 GtkWidget *
83 dlg_button_row_new(const gchar *stock_id_first, ...)
84 {
85     gint        buttons = 0;
86     va_list     stock_id_list;
87     const gchar *stock_id = stock_id_first;
88     GtkWidget   *hbox;
89     GtkWidget   *button_hbox;
90     GtkWidget   *help_hbox;
91     GtkWidget   *button;
92
93     const gchar *ok           = NULL;
94     const gchar *apply        = NULL;
95     const gchar *save         = NULL;
96     const gchar *dont_save    = NULL;
97     const gchar *cancel       = NULL;
98     const gchar *close        = NULL;
99     const gchar *clear        = NULL;
100     const gchar *start        = NULL;
101     const gchar *stop         = NULL;
102     const gchar *create_stat  = NULL;
103     const gchar *help         = NULL;
104     const gchar *print        = NULL;
105     const gchar *find         = NULL;
106     const gchar *jump         = NULL;
107     const gchar *yes          = NULL;
108     const gchar *no           = NULL;
109
110
111     va_start(stock_id_list, stock_id_first);
112
113     /* get all buttons needed */
114     while(stock_id != NULL) {
115         if (strcmp(stock_id, GTK_STOCK_OK) == 0) {
116             ok = stock_id;
117         } else if (strcmp(stock_id, ETHEREAL_STOCK_CREATE_STAT) == 0) {
118             create_stat = stock_id;
119         } else if (strcmp(stock_id, GTK_STOCK_APPLY) == 0) {
120             apply = stock_id;
121         } else if (strcmp(stock_id, GTK_STOCK_SAVE) == 0) {
122             save = stock_id;
123         } else if (strcmp(stock_id, ETHEREAL_STOCK_DONT_SAVE) == 0) {
124                 dont_save = stock_id;  
125         } else if (strcmp(stock_id, GTK_STOCK_CANCEL) == 0) {
126             cancel = stock_id;
127         } else if (strcmp(stock_id, GTK_STOCK_CLOSE) == 0) {
128             close = stock_id;
129         } else if (strcmp(stock_id, GTK_STOCK_CLEAR) == 0) {
130             clear = stock_id;
131 #ifdef HAVE_LIBPCAP
132         } else if (strcmp(stock_id, ETHEREAL_STOCK_CAPTURE_START) == 0) {
133             start = stock_id;
134 #endif /* HAVE_LIBPCAP */
135         } else if (strcmp(stock_id, GTK_STOCK_STOP) == 0) {
136             stop = stock_id;
137         } else if (strcmp(stock_id, GTK_STOCK_HELP) == 0) {
138             help = stock_id;
139         } else if (strcmp(stock_id, GTK_STOCK_PRINT) == 0) {
140             print = stock_id;
141         } else if (strcmp(stock_id, GTK_STOCK_FIND) == 0) {
142             find = stock_id;
143         } else if (strcmp(stock_id, GTK_STOCK_JUMP_TO) == 0) {
144             jump = stock_id;
145         } else if (strcmp(stock_id, GTK_STOCK_YES) == 0) {
146             yes = stock_id;
147         } else if (strcmp(stock_id, GTK_STOCK_NO) == 0) {
148             no = stock_id;
149         } else if (strcmp(stock_id, ETHEREAL_STOCK_FILTER_OUT_STREAM) == 0) {
150             start = stock_id;
151         } else {
152             /* we don't know that button! */
153             g_assert_not_reached();
154         }
155         buttons++;
156         stock_id = va_arg(stock_id_list, gchar *);
157     }
158     va_end(stock_id_list);
159
160     hbox = gtk_hbox_new(FALSE, 0);
161     gtk_widget_show(hbox);
162
163     button_hbox = gtk_hbutton_box_new();
164     gtk_box_pack_end(GTK_BOX(hbox), button_hbox, TRUE, TRUE, 0);
165     gtk_widget_show(button_hbox);
166
167     help_hbox = gtk_hbutton_box_new();
168     gtk_box_pack_end(GTK_BOX(hbox), help_hbox, FALSE, FALSE, 0);
169     gtk_widget_show(help_hbox);
170
171     if (buttons == 0) {
172         /* if no buttons wanted, simply do nothing */
173         return hbox;
174     }
175
176     if (buttons == 1) {
177         /* if only one button, simply put it in the middle (default) */
178         dlg_button_new(hbox, button_hbox, stock_id_first);
179         return hbox;
180     }
181
182     /* do we have a help button? -> special handling for it */
183     if (help) {
184         button = BUTTON_NEW_FROM_STOCK(help);
185         GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
186         OBJECT_SET_DATA(hbox, help, button);
187         gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
188         gtk_widget_show(button);
189         buttons--;
190     }
191
192     /* if more than one button, sort buttons from left to right */
193     /* (the whole button cluster will then be right aligned) */
194     gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
195     gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
196
197 /* GTK+ 1.3 and later - on Win32, we use 1.3[.x] or 2.x, not 1.2[.x] */
198 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2
199     /* beware: sequence of buttons are important! */
200
201     /* XXX: this can be implemented more elegant of course, but it works as it should */
202     if (buttons == 2) {
203         if (ok && cancel) {
204             dlg_button_new(hbox, button_hbox, cancel);
205             dlg_button_new(hbox, button_hbox, ok);
206             return hbox;
207         }
208         if (print && cancel) {
209             dlg_button_new(hbox, button_hbox, cancel);
210             dlg_button_new(hbox, button_hbox, print);
211             return hbox;
212         }
213         if (find && cancel) {
214             dlg_button_new(hbox, button_hbox, cancel);
215             dlg_button_new(hbox, button_hbox, find);
216             return hbox;
217         }
218         if (jump && cancel) {
219             dlg_button_new(hbox, button_hbox, cancel);
220             dlg_button_new(hbox, button_hbox, jump);
221             return hbox;
222         }
223         if (save && cancel) {
224             dlg_button_new(hbox, button_hbox, cancel);
225             dlg_button_new(hbox, button_hbox, save);
226             return hbox;
227         }
228         if (ok && clear) {
229             dlg_button_new(hbox, button_hbox, clear);
230             dlg_button_new(hbox, button_hbox, ok);
231             return hbox;
232         }
233         if (save && close) {
234             dlg_button_new(hbox, button_hbox, close);
235             dlg_button_new(hbox, button_hbox, save);
236             return hbox;
237         }
238         if (create_stat && cancel) {
239             dlg_button_new(hbox, button_hbox, cancel);
240             dlg_button_new(hbox, button_hbox, create_stat);
241             return hbox;
242         }
243         if (start && cancel) {
244             dlg_button_new(hbox, button_hbox, cancel);
245             dlg_button_new(hbox, button_hbox, start);
246             return hbox;
247         }
248     }
249     if (buttons == 3) {
250         if (ok && save && close) {
251             dlg_button_new(hbox, button_hbox, save);
252             dlg_button_new(hbox, button_hbox, close);
253             dlg_button_new(hbox, button_hbox, ok);
254             return hbox;
255         }
256         if (ok && apply && cancel) {
257             dlg_button_new(hbox, button_hbox, apply);
258             dlg_button_new(hbox, button_hbox, cancel);
259             dlg_button_new(hbox, button_hbox, ok);
260             return hbox;
261         }
262         if (apply && save && close) {
263             dlg_button_new(hbox, button_hbox, save);
264             dlg_button_new(hbox, button_hbox, close);
265             dlg_button_new(hbox, button_hbox, apply);
266             return hbox;
267         }
268         if (yes && no && cancel) {
269             dlg_button_new(hbox, button_hbox, no);
270             dlg_button_new(hbox, button_hbox, cancel);
271             dlg_button_new(hbox, button_hbox, yes);
272             return hbox;
273         }
274         if (save && dont_save && cancel) {
275                 dlg_button_new(hbox, button_hbox, dont_save);
276                 dlg_button_new(hbox, button_hbox, cancel);
277                 dlg_button_new(hbox, button_hbox, save);
278                 return hbox;
279         }
280     }
281     if (buttons == 4) {
282         if (ok && apply && save && cancel) {
283             dlg_button_new(hbox, button_hbox, save);
284             dlg_button_new(hbox, button_hbox, apply);
285             dlg_button_new(hbox, button_hbox, cancel);
286             dlg_button_new(hbox, button_hbox, ok);
287             return hbox;
288         }
289         if (ok && apply && save && close) {
290             dlg_button_new(hbox, button_hbox, save);
291             dlg_button_new(hbox, button_hbox, apply);
292             dlg_button_new(hbox, button_hbox, close);
293             dlg_button_new(hbox, button_hbox, ok);
294             return hbox;
295         }
296     }
297 #endif
298
299     /* beware: sequence of buttons is important! */
300     if (ok      != NULL) dlg_button_new(hbox, button_hbox, ok);
301     if (jump    != NULL) dlg_button_new(hbox, button_hbox, jump);
302     if (find    != NULL) dlg_button_new(hbox, button_hbox, find);
303     if (print   != NULL) dlg_button_new(hbox, button_hbox, print);
304     if (create_stat != NULL) dlg_button_new(hbox, button_hbox, create_stat);
305     if (apply   != NULL) dlg_button_new(hbox, button_hbox, apply);
306     if (yes     != NULL) dlg_button_new(hbox, button_hbox, yes);
307     if (no      != NULL) dlg_button_new(hbox, button_hbox, no);
308     if (save    != NULL) dlg_button_new(hbox, button_hbox, save);
309     if (dont_save != NULL) dlg_button_new(hbox, button_hbox, dont_save);
310     if (start   != NULL) dlg_button_new(hbox, button_hbox, start);
311     if (stop    != NULL) dlg_button_new(hbox, button_hbox, stop);
312     if (close   != NULL) dlg_button_new(hbox, button_hbox, close);
313     if (clear   != NULL) dlg_button_new(hbox, button_hbox, clear);
314     if (cancel  != NULL) dlg_button_new(hbox, button_hbox, cancel);
315
316     /* GTK2: we don't know that button combination, add it to the above list! */
317     /* g_assert_not_reached(); */
318     return hbox;
319 }
320
321
322 /* this is called, when a dialog was closed */
323 static void dlg_destroy_cb(GtkWidget *dialog _U_, gpointer data _U_)
324 {
325 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 4
326     if(top_level) {
327         /* bring main window back to front (workaround for a bug in win32 GTK2.x)
328            XXX - do this only on Windows? */
329         gtk_window_present(GTK_WINDOW(top_level));
330     }
331 #endif
332 }
333
334
335 /* Create a dialog box window that belongs to Wireshark's main window. */
336 GtkWidget *
337 dlg_window_new(const gchar *title)
338 {
339   GtkWidget *win;
340
341 #if GTK_MAJOR_VERSION < 2
342   win = window_new(GTK_WINDOW_DIALOG, title);
343 #else
344   win = window_new(GTK_WINDOW_TOPLEVEL, title);
345 #endif
346
347   /*
348    * XXX - if we're running in the capture child process, we can't easily
349    * make this window transient for the main process's window.  We just
350    * punt here.
351    *
352    * Perhaps the child process should only capture packets, write them to
353    * a file, and somehow notify the parent process and let *it* do all
354    * the GUI work.  If we can do that efficiently (so that we don't drop
355    * more packets), perhaps we can also do so even when we're *not* doing
356    * an "Update list of packets in real time" capture.  That'd let the
357    * child process run set-UID on platforms where you need that in order
358    * to capture, and might also simplify the job of having the GUI main
359    * loop wait both for user input and packet arrival.
360    */
361   if (top_level) {
362     gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(top_level));
363   }
364
365   SIGNAL_CONNECT(win, "destroy", dlg_destroy_cb, NULL);
366
367   return win;
368 }
369
370 /* Set the "activate" signal for a widget to call a routine to
371    activate the "OK" button for a dialog box.
372
373    XXX - there should be a way to specify that a GtkEntry widget
374    shouldn't itself handle the Return key, but should let it be
375    passed on to the parent, so that you don't have to do this
376    by hand for every GtkEntry widget in a dialog box, but, alas,
377    there isn't.  (Does this problem exist for other widgets?
378    I.e., are there any others that seize the Return key? */
379 void
380 dlg_set_activate(GtkWidget *widget, GtkWidget *ok_button)
381 {
382   SIGNAL_CONNECT(widget, "activate", dlg_activate, ok_button);
383 }
384
385 static void
386 dlg_activate (GtkWidget *widget _U_, gpointer ok_button)
387 {
388   gtk_widget_activate(GTK_WIDGET(ok_button));
389 }
390
391 #if GTK_MAJOR_VERSION < 2
392 /* Sigh.  GTK+ appears not to acknowledge that it should be possible
393    to attach mnemonics to anything other than menu items; provide
394    routines to create radio and check buttons with labels that
395    include mnemonics.  */
396 typedef struct {
397         GtkWidget *button;
398         GtkAccelGroup *accel_group;
399 } fix_label_args_t;
400
401 static void
402 dlg_fix_label_callback(GtkWidget *label_widget, gpointer data)
403 {
404   fix_label_args_t *args = data;
405   gchar *label;
406   guint accel_key;
407
408   gtk_label_get(GTK_LABEL(label_widget), &label);
409   accel_key = gtk_label_parse_uline(GTK_LABEL(label_widget), label);
410   if (accel_key != GDK_VoidSymbol) {
411     /* Yes, we have a mnemonic. */
412     gtk_widget_add_accelerator(args->button, "clicked", args->accel_group,
413                                 accel_key, 0, GTK_ACCEL_LOCKED);
414     gtk_widget_add_accelerator(args->button, "clicked", args->accel_group,
415                                 accel_key, GDK_MOD1_MASK, GTK_ACCEL_LOCKED);
416   }
417 }
418
419 static void
420 dlg_fix_button_label(GtkWidget *button, GtkAccelGroup *accel_group)
421 {
422   fix_label_args_t args;
423
424   args.button = button;
425   args.accel_group = accel_group;
426   gtk_container_foreach(GTK_CONTAINER(button), dlg_fix_label_callback, &args);
427 }
428
429 GtkWidget *
430 dlg_radio_button_new_with_label_with_mnemonic(GSList *group,
431                 const gchar *label, GtkAccelGroup *accel_group)
432 {
433   GtkWidget *radio_button;
434
435   radio_button = gtk_radio_button_new_with_label (group, label);
436   dlg_fix_button_label(radio_button, accel_group);
437   return radio_button;
438 }
439
440 GtkWidget *
441 dlg_check_button_new_with_label_with_mnemonic(const gchar *label,
442                         GtkAccelGroup *accel_group)
443 {
444   GtkWidget *check_button;
445
446   check_button = gtk_check_button_new_with_label (label);
447   dlg_fix_button_label(check_button, accel_group);
448   return check_button;
449 }
450
451 GtkWidget *
452 dlg_toggle_button_new_with_label_with_mnemonic(const gchar *label,
453                         GtkAccelGroup *accel_group)
454 {
455   GtkWidget *toggle_button;
456
457   toggle_button = gtk_toggle_button_new_with_label (label);
458   dlg_fix_button_label(toggle_button, accel_group);
459   return toggle_button;
460 }
461 #endif