2 * Utilities to use when constructing dialogs
4 * $Id: dlg_utils.c,v 1.29 2004/05/21 00:18:46 guy Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
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"
34 #include "dlg_utils.h"
35 #include "compat_macros.h"
42 dlg_activate (GtkWidget *widget, gpointer ok_button);
45 dlg_key_press (GtkWidget *widget, GdkEventKey *event, gpointer cancel_button);
49 /* create a button for the button row (helper for dlg_button_row_new) */
51 dlg_button_new(GtkWidget *hbox, GtkWidget *button_hbox, gchar *stock_id)
55 button = BUTTON_NEW_FROM_STOCK(stock_id);
56 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
57 OBJECT_SET_DATA(hbox, stock_id, button);
58 gtk_box_pack_end(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
59 gtk_widget_show(button);
64 /* create a button row for a dialog */
66 /* The purpose of this is, to have one place available, where all button rows
67 * from all dialogs are layouted. This will:
69 * a.) keep the button layout more consistent over the different dialogs
70 * b.) being able to switch between different button layouts, e.g.:
71 * GTK1 (e.g. win32) "OK" "Apply" "Cancel"
72 * GTK2 (e.g. GNOME) "Apply" "Cancel" "OK"
75 dlg_button_row_new(gchar *stock_id_first, ...)
78 va_list stock_id_list;
79 gchar *stock_id = stock_id_first;
81 GtkWidget *button_hbox;
92 gchar *create_stat = NULL;
101 va_start(stock_id_list, stock_id_first);
103 /* get all buttons needed */
104 while(stock_id != NULL) {
105 if (strcmp(stock_id, GTK_STOCK_OK) == 0) {
107 } else if (strcmp(stock_id, ETHEREAL_STOCK_CREATE_STAT) == 0) {
108 create_stat = stock_id;
109 } else if (strcmp(stock_id, GTK_STOCK_APPLY) == 0) {
111 } else if (strcmp(stock_id, GTK_STOCK_SAVE) == 0) {
113 } else if (strcmp(stock_id, GTK_STOCK_CANCEL) == 0) {
115 } else if (strcmp(stock_id, GTK_STOCK_CLOSE) == 0) {
117 } else if (strcmp(stock_id, GTK_STOCK_CLEAR) == 0) {
119 } else if (strcmp(stock_id, GTK_STOCK_STOP) == 0) {
121 } else if (strcmp(stock_id, GTK_STOCK_HELP) == 0) {
123 } else if (strcmp(stock_id, GTK_STOCK_PRINT) == 0) {
125 } else if (strcmp(stock_id, GTK_STOCK_FIND) == 0) {
127 } else if (strcmp(stock_id, GTK_STOCK_JUMP_TO) == 0) {
129 } else if (strcmp(stock_id, GTK_STOCK_YES) == 0) {
131 } else if (strcmp(stock_id, GTK_STOCK_NO) == 0) {
134 /* we don't know that button! */
135 g_assert_not_reached();
138 stock_id = va_arg(stock_id_list, gchar *);
140 va_end(stock_id_list);
142 /* we should have at least one button */
146 hbox = gtk_hbox_new(FALSE, 0);
147 gtk_widget_show(hbox);
149 button_hbox = gtk_hbutton_box_new();
150 gtk_box_pack_end(GTK_BOX(hbox), button_hbox, TRUE, TRUE, 0);
151 gtk_widget_show(button_hbox);
153 help_hbox = gtk_hbutton_box_new();
154 gtk_box_pack_end(GTK_BOX(hbox), help_hbox, FALSE, FALSE, 0);
155 gtk_widget_show(help_hbox);
158 /* if only one button, simply put it in the middle (default) */
159 dlg_button_new(hbox, button_hbox, stock_id_first);
163 /* do we have a help button? -> special handling for it */
165 button = BUTTON_NEW_FROM_STOCK(help);
166 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
167 OBJECT_SET_DATA(hbox, help, button);
168 gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
169 gtk_widget_show(button);
173 /* if more than one button, sort buttons from left to right */
174 /* (the whole button cluster will then be right aligned) */
175 gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
176 gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
178 #if !WIN32 && GTK_MAJOR_VERSION >= 2
179 /* beware: sequence of buttons are important! */
181 /* XXX: this can be implemented more elegant of course, but it works as it should */
184 dlg_button_new(hbox, button_hbox, cancel);
185 dlg_button_new(hbox, button_hbox, ok);
188 if (print && cancel) {
189 dlg_button_new(hbox, button_hbox, cancel);
190 dlg_button_new(hbox, button_hbox, print);
193 if (find && cancel) {
194 dlg_button_new(hbox, button_hbox, cancel);
195 dlg_button_new(hbox, button_hbox, find);
198 if (jump && cancel) {
199 dlg_button_new(hbox, button_hbox, cancel);
200 dlg_button_new(hbox, button_hbox, jump);
203 if (save && cancel) {
204 dlg_button_new(hbox, button_hbox, cancel);
205 dlg_button_new(hbox, button_hbox, save);
209 dlg_button_new(hbox, button_hbox, clear);
210 dlg_button_new(hbox, button_hbox, ok);
214 dlg_button_new(hbox, button_hbox, close);
215 dlg_button_new(hbox, button_hbox, save);
218 if (create_stat && cancel) {
219 dlg_button_new(hbox, button_hbox, cancel);
220 dlg_button_new(hbox, button_hbox, create_stat);
225 if (ok && save && close) {
226 dlg_button_new(hbox, button_hbox, save);
227 dlg_button_new(hbox, button_hbox, close);
228 dlg_button_new(hbox, button_hbox, ok);
231 if (ok && apply && cancel) {
232 dlg_button_new(hbox, button_hbox, apply);
233 dlg_button_new(hbox, button_hbox, cancel);
234 dlg_button_new(hbox, button_hbox, ok);
237 if (apply && save && close) {
238 dlg_button_new(hbox, button_hbox, save);
239 dlg_button_new(hbox, button_hbox, close);
240 dlg_button_new(hbox, button_hbox, apply);
243 if (yes && no && cancel) {
244 dlg_button_new(hbox, button_hbox, yes);
245 dlg_button_new(hbox, button_hbox, no);
246 dlg_button_new(hbox, button_hbox, cancel);
251 if (ok && apply && save && cancel) {
252 dlg_button_new(hbox, button_hbox, save);
253 dlg_button_new(hbox, button_hbox, apply);
254 dlg_button_new(hbox, button_hbox, cancel);
255 dlg_button_new(hbox, button_hbox, ok);
258 if (ok && apply && save && close) {
259 dlg_button_new(hbox, button_hbox, save);
260 dlg_button_new(hbox, button_hbox, apply);
261 dlg_button_new(hbox, button_hbox, close);
262 dlg_button_new(hbox, button_hbox, ok);
268 /* beware: sequence of buttons is important! */
269 if (ok != NULL) dlg_button_new(hbox, button_hbox, ok);
270 if (jump != NULL) dlg_button_new(hbox, button_hbox, jump);
271 if (find != NULL) dlg_button_new(hbox, button_hbox, find);
272 if (print != NULL) dlg_button_new(hbox, button_hbox, print);
273 if (create_stat != NULL) dlg_button_new(hbox, button_hbox, create_stat);
274 if (apply != NULL) dlg_button_new(hbox, button_hbox, apply);
275 if (yes != NULL) dlg_button_new(hbox, button_hbox, yes);
276 if (no != NULL) dlg_button_new(hbox, button_hbox, no);
277 if (save != NULL) dlg_button_new(hbox, button_hbox, save);
278 if (stop != NULL) dlg_button_new(hbox, button_hbox, stop);
279 if (close != NULL) dlg_button_new(hbox, button_hbox, close);
280 if (clear != NULL) dlg_button_new(hbox, button_hbox, clear);
281 if (cancel != NULL) dlg_button_new(hbox, button_hbox, cancel);
283 /* GTK2: we don't know that button combination, add it to the above list! */
284 /* g_assert_not_reached(); */
289 #if GTK_MAJOR_VERSION >= 2
290 /* this is called, when a dialog was closed */
291 void on_dialog_destroyed(GtkWidget *dialog _U_, gpointer data)
293 /* bring main window back to front (workaround for a bug in win32 GTK2.x)
294 XXX - do this only on Windows? */
295 gtk_window_present(GTK_WINDOW(data));
300 /* Create a dialog box window that belongs to Ethereal's main window. */
302 dlg_window_new(const gchar *title)
306 #if GTK_MAJOR_VERSION < 2
307 win = window_new(GTK_WINDOW_DIALOG, title);
309 win = window_new(GTK_WINDOW_TOPLEVEL, title);
312 * XXX - if we're running in the capture child process, we can't easily
313 * make this window transient for the main process's window. We just
316 * Perhaps the child process should only capture packets, write them to
317 * a file, and somehow notify the parent process and let *it* do all
318 * the GUI work. If we can do that efficiently (so that we don't drop
319 * more packets), perhaps we can also do so even when we're *not* doing
320 * an "Update list of packets in real time" capture. That'd let the
321 * child process run set-UID on platforms where you need that in order
322 * to capture, and might also simplify the job of having the GUI main
323 * loop wait both for user input and packet arrival.
326 gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(top_level));
328 #if GTK_MAJOR_VERSION >= 2
329 gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);
331 SIGNAL_CONNECT(win, "destroy", on_dialog_destroyed, top_level);
336 /* Create a file selection dialog box window that belongs to Ethereal's
338 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
340 file_selection_new(const gchar *title, file_selection_action_t action)
343 GtkFileChooserAction gtk_action;
344 const gchar *ok_button_text;
348 case FILE_SELECTION_OPEN:
349 gtk_action = GTK_FILE_CHOOSER_ACTION_OPEN;
350 ok_button_text = GTK_STOCK_OPEN;
353 case FILE_SELECTION_SAVE:
354 gtk_action = GTK_FILE_CHOOSER_ACTION_SAVE;
355 ok_button_text = GTK_STOCK_SAVE;
359 g_assert_not_reached();
361 ok_button_text = NULL;
364 win = gtk_file_chooser_dialog_new(title, GTK_WINDOW(top_level), gtk_action,
365 ok_button_text, GTK_RESPONSE_ACCEPT,
366 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
372 file_selection_new(const gchar *title, file_selection_action_t action _U_)
376 win = gtk_file_selection_new(title);
377 #if GTK_MAJOR_VERSION >= 2
378 gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);
380 gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(top_level));
385 /* Set the current folder for a file selection dialog. */
387 file_selection_set_current_folder(GtkWidget *fs, const gchar *filename)
389 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
390 return gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs), filename);
392 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), filename);
397 /* Set the "extra" widget for a file selection dialog, with user-supplied
400 file_selection_set_extra_widget(GtkWidget *fs, GtkWidget *extra)
402 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
403 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(fs), extra);
405 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(fs)->action_area), extra,
410 /* Set the "activate" signal for a widget to call a routine to
411 activate the "OK" button for a dialog box.
413 XXX - there should be a way to specify that a GtkEntry widget
414 shouldn't itself handle the Return key, but should let it be
415 passed on to the parent, so that you don't have to do this
416 by hand for every GtkEntry widget in a dialog box, but, alas,
417 there isn't. (Does this problem exist for other widgets?
418 I.e., are there any others that seize the Return key? */
420 dlg_set_activate(GtkWidget *widget, GtkWidget *ok_button)
422 SIGNAL_CONNECT(widget, "activate", dlg_activate, ok_button);
426 dlg_activate (GtkWidget *widget _U_, gpointer ok_button)
428 gtk_widget_activate(GTK_WIDGET(ok_button));
431 /* Set the "key_press_event" signal for a top-level dialog window to
432 call a routine to activate the "Cancel" button for a dialog box if
433 the key being pressed is the <Esc> key.
435 XXX - there should be a GTK+ widget that'll do that for you, and
436 let you specify a "Cancel" button. It should also not impose
437 a requirement that there be a separator in the dialog box, as
438 the GtkDialog widget does; the visual convention that there's
439 such a separator between the rest of the dialog boxes and buttons
440 such as "OK" and "Cancel" is, for better or worse, not universal
441 (not even in GTK+ - look at the GtkFileSelection dialog!). */
443 dlg_set_cancel(GtkWidget *widget, GtkWidget *cancel_button)
445 SIGNAL_CONNECT(widget, "key_press_event", dlg_key_press, cancel_button);
449 dlg_key_press (GtkWidget *widget, GdkEventKey *event, gpointer cancel_button)
451 g_return_val_if_fail (widget != NULL, FALSE);
452 g_return_val_if_fail (event != NULL, FALSE);
454 if (event->keyval == GDK_Escape) {
455 gtk_widget_activate(GTK_WIDGET(cancel_button));
462 #if GTK_MAJOR_VERSION < 2
463 /* Sigh. GTK+ appears not to acknowledge that it should be possible
464 to attach mnemonics to anything other than menu items; provide
465 routines to create radio and check buttons with labels that
466 include mnemonics. */
469 GtkAccelGroup *accel_group;
473 dlg_fix_label_callback(GtkWidget *label_widget, gpointer data)
475 fix_label_args_t *args = data;
479 gtk_label_get(GTK_LABEL(label_widget), &label);
480 accel_key = gtk_label_parse_uline(GTK_LABEL(label_widget), label);
481 if (accel_key != GDK_VoidSymbol) {
482 /* Yes, we have a mnemonic. */
483 gtk_widget_add_accelerator(args->button, "clicked", args->accel_group,
484 accel_key, 0, GTK_ACCEL_LOCKED);
485 gtk_widget_add_accelerator(args->button, "clicked", args->accel_group,
486 accel_key, GDK_MOD1_MASK, GTK_ACCEL_LOCKED);
491 dlg_fix_button_label(GtkWidget *button, GtkAccelGroup *accel_group)
493 fix_label_args_t args;
495 args.button = button;
496 args.accel_group = accel_group;
497 gtk_container_foreach(GTK_CONTAINER(button), dlg_fix_label_callback, &args);
501 dlg_radio_button_new_with_label_with_mnemonic(GSList *group,
502 const gchar *label, GtkAccelGroup *accel_group)
504 GtkWidget *radio_button;
506 radio_button = gtk_radio_button_new_with_label (group, label);
507 dlg_fix_button_label(radio_button, accel_group);
512 dlg_check_button_new_with_label_with_mnemonic(const gchar *label,
513 GtkAccelGroup *accel_group)
515 GtkWidget *check_button;
517 check_button = gtk_check_button_new_with_label (label);
518 dlg_fix_button_label(check_button, accel_group);
523 dlg_toggle_button_new_with_label_with_mnemonic(const gchar *label,
524 GtkAccelGroup *accel_group)
526 GtkWidget *toggle_button;
528 toggle_button = gtk_toggle_button_new_with_label (label);
529 dlg_fix_button_label(toggle_button, accel_group);
530 return toggle_button;