2 * Utilities to use when constructing dialogs
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
30 #include <gdk/gdkkeysyms.h>
32 #include "gtkglobals.h"
33 #include "gui_utils.h"
34 #include "dlg_utils.h"
35 #include "compat_macros.h"
41 dlg_activate (GtkWidget *widget, gpointer ok_button);
43 /* create a button for the button row (helper for dlg_button_row_new) */
45 dlg_button_new(GtkWidget *hbox, GtkWidget *button_hbox, const gchar *stock_id)
49 button = BUTTON_NEW_FROM_STOCK(stock_id);
50 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
51 OBJECT_SET_DATA(hbox, stock_id, button);
52 gtk_box_pack_end(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
53 gtk_widget_show(button);
58 * Set the focus and default for the nth item in a button row, with
59 * 0 being the first item.
61 #define BUTTON_HBOX_KEY "button_hbox"
63 dlg_button_focus_nth(GtkWidget *hbox, gint focus_item) {
64 GtkWidget *button_hbox, *button;
71 button_hbox = OBJECT_GET_DATA(hbox, BUTTON_HBOX_KEY);
72 children = gtk_container_children(GTK_CONTAINER(button_hbox));
75 if (cur_item == focus_item) {
76 button = children->data;
77 gtk_widget_grab_focus(button);
78 gtk_widget_grab_default(button);
81 children = g_list_next(children);
85 g_list_free(children);
88 /* create a button row for a dialog */
90 /* The purpose of this is, to have one place available, where all button rows
91 * from all dialogs are laid out. This will:
93 * a.) keep the button layout more consistent over the different dialogs
94 * b.) being able to switch between different button layouts, e.g.:
95 * GTK1 (e.g. win32) "OK" "Apply" "Cancel"
96 * GTK2 (e.g. GNOME) "Apply" "Cancel" "OK"
99 dlg_button_row_new(const gchar *stock_id_first, ...)
102 va_list stock_id_list;
103 const gchar *stock_id = stock_id_first;
105 GtkWidget *button_hbox;
106 GtkWidget *help_hbox;
109 const gchar *apply = NULL;
110 const gchar *cancel = NULL;
111 const gchar *cap_start = NULL;
112 const gchar *cap_stop = NULL;
113 const gchar *clear = NULL;
114 const gchar *close = NULL;
115 const gchar *copy = NULL;
116 const gchar *create_stat = NULL;
117 const gchar *delete = NULL;
118 const gchar *dont_save = NULL;
119 const gchar *filter_stream= NULL;
120 const gchar *find = NULL;
121 const gchar *help = NULL;
122 const gchar *jump = NULL;
123 const gchar *no = NULL;
124 const gchar *ok = NULL;
125 const gchar *print = NULL;
126 const gchar *save = NULL;
127 const gchar *stop = NULL;
128 const gchar *yes = NULL;
131 va_start(stock_id_list, stock_id_first);
133 /* get all buttons needed */
134 while(stock_id != NULL) {
135 if (strcmp(stock_id, GTK_STOCK_OK) == 0) {
137 } else if (strcmp(stock_id, WIRESHARK_STOCK_CREATE_STAT) == 0) {
138 create_stat = stock_id;
139 } else if (strcmp(stock_id, GTK_STOCK_APPLY) == 0) {
141 } else if (strcmp(stock_id, GTK_STOCK_SAVE) == 0) {
143 } else if (strcmp(stock_id, WIRESHARK_STOCK_DONT_SAVE) == 0) {
144 dont_save = stock_id;
145 } else if (strcmp(stock_id, GTK_STOCK_CANCEL) == 0) {
147 } else if (strcmp(stock_id, GTK_STOCK_CLOSE) == 0) {
149 } else if (strcmp(stock_id, GTK_STOCK_CLEAR) == 0) {
152 } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_START) == 0) {
153 cap_start = stock_id;
154 } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_STOP) == 0) {
156 #endif /* HAVE_LIBPCAP */
157 } else if (strcmp(stock_id, GTK_STOCK_STOP) == 0) {
159 } else if (strcmp(stock_id, GTK_STOCK_HELP) == 0) {
161 } else if (strcmp(stock_id, GTK_STOCK_PRINT) == 0) {
163 } else if (strcmp(stock_id, GTK_STOCK_FIND) == 0) {
165 } else if (strcmp(stock_id, GTK_STOCK_JUMP_TO) == 0) {
167 } else if (strcmp(stock_id, GTK_STOCK_YES) == 0) {
169 } else if (strcmp(stock_id, GTK_STOCK_NO) == 0) {
171 } else if (strcmp(stock_id, WIRESHARK_STOCK_FILTER_OUT_STREAM) == 0) {
172 filter_stream = stock_id;
173 } else if (strcmp(stock_id, GTK_STOCK_DELETE) == 0) {
175 } else if (strcmp(stock_id, GTK_STOCK_COPY) == 0) {
178 /* we don't know that button! */
179 g_assert_not_reached();
182 stock_id = va_arg(stock_id_list, gchar *);
184 va_end(stock_id_list);
186 hbox = gtk_hbox_new(FALSE, 0);
187 gtk_widget_show(hbox);
189 button_hbox = gtk_hbutton_box_new();
190 gtk_box_pack_end(GTK_BOX(hbox), button_hbox, TRUE, TRUE, 0);
191 OBJECT_SET_DATA(hbox, BUTTON_HBOX_KEY, button_hbox);
192 gtk_widget_show(button_hbox);
194 help_hbox = gtk_hbutton_box_new();
195 gtk_box_pack_end(GTK_BOX(hbox), help_hbox, FALSE, FALSE, 0);
196 gtk_widget_show(help_hbox);
199 /* if no buttons wanted, simply do nothing */
204 /* if only one button, simply put it in the middle (default) */
205 dlg_button_new(hbox, button_hbox, stock_id_first);
209 /* do we have a help button? -> special handling for it */
211 button = BUTTON_NEW_FROM_STOCK(help);
212 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
213 OBJECT_SET_DATA(hbox, help, button);
214 gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
215 gtk_widget_show(button);
219 /* if more than one button, sort buttons from left to right */
220 /* (the whole button cluster will then be right aligned) */
221 gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
222 gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
224 /* GTK+ 1.3 and later - on Win32, we use 1.3[.x] or 2.x, not 1.2[.x] */
225 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2
226 /* beware: sequence of buttons are important! */
228 /* XXX: this can be implemented more elegant of course, but it works as it should */
231 dlg_button_new(hbox, button_hbox, cancel);
232 dlg_button_new(hbox, button_hbox, ok);
235 if (print && cancel) {
236 dlg_button_new(hbox, button_hbox, cancel);
237 dlg_button_new(hbox, button_hbox, print);
240 if (find && cancel) {
241 dlg_button_new(hbox, button_hbox, cancel);
242 dlg_button_new(hbox, button_hbox, find);
245 if (jump && cancel) {
246 dlg_button_new(hbox, button_hbox, cancel);
247 dlg_button_new(hbox, button_hbox, jump);
250 if (save && cancel) {
251 dlg_button_new(hbox, button_hbox, cancel);
252 dlg_button_new(hbox, button_hbox, save);
256 dlg_button_new(hbox, button_hbox, clear);
257 dlg_button_new(hbox, button_hbox, ok);
261 dlg_button_new(hbox, button_hbox, close);
262 dlg_button_new(hbox, button_hbox, save);
265 if (create_stat && cancel) {
266 dlg_button_new(hbox, button_hbox, cancel);
267 dlg_button_new(hbox, button_hbox, create_stat);
270 if (cap_start && cancel) {
271 dlg_button_new(hbox, button_hbox, cancel);
272 dlg_button_new(hbox, button_hbox, cap_start);
275 if (cap_stop && cancel) {
276 dlg_button_new(hbox, button_hbox, cancel);
277 dlg_button_new(hbox, button_hbox, cap_stop);
280 if (delete && cancel) {
281 dlg_button_new(hbox, button_hbox, cancel);
282 dlg_button_new(hbox, button_hbox, delete);
287 if (ok && save && close) {
288 dlg_button_new(hbox, button_hbox, save);
289 dlg_button_new(hbox, button_hbox, close);
290 dlg_button_new(hbox, button_hbox, ok);
293 if (ok && apply && cancel) {
294 dlg_button_new(hbox, button_hbox, apply);
295 dlg_button_new(hbox, button_hbox, cancel);
296 dlg_button_new(hbox, button_hbox, ok);
299 if (apply && save && close) {
300 dlg_button_new(hbox, button_hbox, save);
301 dlg_button_new(hbox, button_hbox, close);
302 dlg_button_new(hbox, button_hbox, apply);
305 if (yes && no && cancel) {
306 dlg_button_new(hbox, button_hbox, no);
307 dlg_button_new(hbox, button_hbox, cancel);
308 dlg_button_new(hbox, button_hbox, yes);
311 if (save && dont_save && cancel) {
312 dlg_button_new(hbox, button_hbox, dont_save);
313 dlg_button_new(hbox, button_hbox, cancel);
314 dlg_button_new(hbox, button_hbox, save);
319 if (ok && apply && save && cancel) {
320 dlg_button_new(hbox, button_hbox, save);
321 dlg_button_new(hbox, button_hbox, apply);
322 dlg_button_new(hbox, button_hbox, cancel);
323 dlg_button_new(hbox, button_hbox, ok);
326 if (ok && apply && save && close) {
327 dlg_button_new(hbox, button_hbox, save);
328 dlg_button_new(hbox, button_hbox, apply);
329 dlg_button_new(hbox, button_hbox, close);
330 dlg_button_new(hbox, button_hbox, ok);
336 /* beware: sequence of buttons is important! */
337 if (ok != NULL) dlg_button_new(hbox, button_hbox, ok);
338 if (delete != NULL) dlg_button_new(hbox, button_hbox, delete);
339 if (jump != NULL) dlg_button_new(hbox, button_hbox, jump);
340 if (find != NULL) dlg_button_new(hbox, button_hbox, find);
341 if (print != NULL) dlg_button_new(hbox, button_hbox, print);
342 if (copy != NULL) dlg_button_new(hbox, button_hbox, copy);
343 if (create_stat != NULL) dlg_button_new(hbox, button_hbox, create_stat);
344 if (apply != NULL) dlg_button_new(hbox, button_hbox, apply);
345 if (yes != NULL) dlg_button_new(hbox, button_hbox, yes);
346 if (no != NULL) dlg_button_new(hbox, button_hbox, no);
347 if (save != NULL) dlg_button_new(hbox, button_hbox, save);
348 if (dont_save != NULL) dlg_button_new(hbox, button_hbox, dont_save);
349 if (cap_start != NULL) dlg_button_new(hbox, button_hbox, cap_start);
350 if (cap_stop != NULL) dlg_button_new(hbox, button_hbox, cap_stop);
351 if (stop != NULL) dlg_button_new(hbox, button_hbox, stop);
352 if (close != NULL) dlg_button_new(hbox, button_hbox, close);
353 if (clear != NULL) dlg_button_new(hbox, button_hbox, clear);
354 if (cancel != NULL) dlg_button_new(hbox, button_hbox, cancel);
355 if (filter_stream!= NULL) dlg_button_new(hbox, button_hbox, filter_stream);
357 /* GTK2: we don't know that button combination, add it to the above list! */
358 /* g_assert_not_reached(); */
363 /* this is called, when a dialog was closed */
364 static void dlg_destroy_cb(GtkWidget *dialog _U_, gpointer data _U_)
366 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 4
368 /* bring main window back to front (workaround for a bug in win32 GTK2.x)
369 XXX - do this only on Windows? */
370 gtk_window_present(GTK_WINDOW(top_level));
376 /* Create a dialog box window that belongs to Wireshark's main window. */
378 dlg_window_new(const gchar *title)
382 #if GTK_MAJOR_VERSION < 2
383 win = window_new(GTK_WINDOW_DIALOG, title);
385 win = window_new(GTK_WINDOW_TOPLEVEL, title);
389 * XXX - if we're running in the capture child process, we can't easily
390 * make this window transient for the main process's window. We just
393 * Perhaps the child process should only capture packets, write them to
394 * a file, and somehow notify the parent process and let *it* do all
395 * the GUI work. If we can do that efficiently (so that we don't drop
396 * more packets), perhaps we can also do so even when we're *not* doing
397 * an "Update list of packets in real time" capture. That'd let the
398 * child process run set-UID on platforms where you need that in order
399 * to capture, and might also simplify the job of having the GUI main
400 * loop wait both for user input and packet arrival.
403 gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(top_level));
406 SIGNAL_CONNECT(win, "destroy", dlg_destroy_cb, NULL);
411 /* Set the "activate" signal for a widget to call a routine to
412 activate the "OK" button for a dialog box.
414 XXX - there should be a way to specify that a GtkEntry widget
415 shouldn't itself handle the Return key, but should let it be
416 passed on to the parent, so that you don't have to do this
417 by hand for every GtkEntry widget in a dialog box, but, alas,
418 there isn't. (Does this problem exist for other widgets?
419 I.e., are there any others that seize the Return key? */
421 dlg_set_activate(GtkWidget *widget, GtkWidget *ok_button)
423 SIGNAL_CONNECT(widget, "activate", dlg_activate, ok_button);
427 dlg_activate (GtkWidget *widget _U_, gpointer ok_button)
429 gtk_widget_activate(GTK_WIDGET(ok_button));
432 #if GTK_MAJOR_VERSION < 2
433 /* Sigh. GTK+ appears not to acknowledge that it should be possible
434 to attach mnemonics to anything other than menu items; provide
435 routines to create radio and check buttons with labels that
436 include mnemonics. */
439 GtkAccelGroup *accel_group;
443 dlg_fix_label_callback(GtkWidget *label_widget, gpointer data)
445 fix_label_args_t *args = data;
449 gtk_label_get(GTK_LABEL(label_widget), &label);
450 accel_key = gtk_label_parse_uline(GTK_LABEL(label_widget), label);
451 if (accel_key != GDK_VoidSymbol) {
452 /* Yes, we have a mnemonic. */
453 gtk_widget_add_accelerator(args->button, "clicked", args->accel_group,
454 accel_key, 0, GTK_ACCEL_LOCKED);
455 gtk_widget_add_accelerator(args->button, "clicked", args->accel_group,
456 accel_key, GDK_MOD1_MASK, GTK_ACCEL_LOCKED);
461 dlg_fix_button_label(GtkWidget *button, GtkAccelGroup *accel_group)
463 fix_label_args_t args;
465 args.button = button;
466 args.accel_group = accel_group;
467 gtk_container_foreach(GTK_CONTAINER(button), dlg_fix_label_callback, &args);
471 dlg_radio_button_new_with_label_with_mnemonic(GSList *group,
472 const gchar *label, GtkAccelGroup *accel_group)
474 GtkWidget *radio_button;
476 radio_button = gtk_radio_button_new_with_label (group, label);
477 dlg_fix_button_label(radio_button, accel_group);
482 dlg_check_button_new_with_label_with_mnemonic(const gchar *label,
483 GtkAccelGroup *accel_group)
485 GtkWidget *check_button;
487 check_button = gtk_check_button_new_with_label (label);
488 dlg_fix_button_label(check_button, accel_group);
493 dlg_toggle_button_new_with_label_with_mnemonic(const gchar *label,
494 GtkAccelGroup *accel_group)
496 GtkWidget *toggle_button;
498 toggle_button = gtk_toggle_button_new_with_label (label);
499 dlg_fix_button_label(toggle_button, accel_group);
500 return toggle_button;