2 * Routines for handling column preferences
4 * $Id: column.c,v 1.6 1998/12/22 07:07:09 gram Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@zing.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
48 static GtkWidget *column_l, *chg_bt, *del_bt, *title_te, *fmt_m, *up_bt,
52 #define E_COL_NAME_KEY "column_name"
53 #define E_COL_LBL_KEY "column_label"
54 #define E_COL_CM_KEY "in_col_cancel_mode"
56 static gchar *col_format_to_string(gint);
57 static gchar *col_format_desc(gint);
58 static gint get_column_format_from_str(gchar *str);
59 static void column_sel_list_cb(GtkWidget *, gpointer);
60 static void column_sel_new_cb(GtkWidget *, gpointer);
61 static void column_sel_chg_cb(GtkWidget *, gpointer);
62 static void column_sel_del_cb(GtkWidget *, gpointer);
63 static void column_sel_arrow_cb(GtkWidget *, gpointer);
64 static void column_set_fmt_cb(GtkWidget *, gpointer);
66 /* Given a format number (as defined in ethereal.h), returns its equivalent
69 col_format_to_string(gint fmt) {
70 gchar *slist[] = { "%m", "%t", "%t", "%t", "%s", "%rs", "%us", "%hs",
71 "%rhs", "%uhs", "%ns", "%rns", "%uns", "%d", "%rd",
72 "%ud", "%hd", "%rhd", "%uhd", "%nd", "%rnd", "%und",
73 "%S", "%rS", "%uS", "%D", "%rD", "%uD", "%p", "%i" };
75 if (fmt < 0 || fmt > NUM_COL_FMTS)
81 /* Given a format number (as defined in ethereal.h), returns its
84 col_format_desc(gint fmt) {
85 gchar *dlist[] = { "Number", "Relative time", "Absolute time",
86 "Delta time", "Source address", "Src addr (resolved)",
87 "Src addr (unresolved)", "Hardware src addr",
88 "Hw src addr (resolved)", "Hw src addr (unresolved)",
89 "Network src addr", "Net scr addr (resolved)",
90 "Net src addr (unresolved)", "Destination address",
91 "Dest addr (resolved)", "Dest addr (unresolved)",
92 "Hardware dest addr", "Hw dest addr (resolved)",
93 "Hw dest addr (unresolved)", "Network dest addr",
94 "Net dest addr (resolved)", "Net dest addr (unresolved)",
95 "Source port", "Src port (resolved)",
96 "Src port (unresolved)", "Destination port",
97 "Dest port (resolved)", "Dest port (unresolved)",
98 "Protocol", "Information" };
100 if (fmt < 0 || fmt > NUM_COL_FMTS)
106 /* Marks each array element true if it can be substituted for the given
109 get_column_format_matches(gboolean *fmt_list, gint format) {
112 for (i = 0; i < NUM_COL_FMTS; i++) {
113 /* Get the obvious: the format itself */
116 /* Get any formats lower down on the chain */
119 fmt_list[COL_RES_DL_SRC] = TRUE;
120 fmt_list[COL_RES_NET_SRC] = TRUE;
123 fmt_list[COL_RES_DL_SRC] = TRUE;
124 fmt_list[COL_RES_NET_SRC] = TRUE;
127 fmt_list[COL_UNRES_DL_SRC] = TRUE;
128 fmt_list[COL_UNRES_NET_SRC] = TRUE;
131 fmt_list[COL_RES_DL_DST] = TRUE;
132 fmt_list[COL_RES_NET_DST] = TRUE;
135 fmt_list[COL_RES_DL_DST] = TRUE;
136 fmt_list[COL_RES_NET_DST] = TRUE;
139 fmt_list[COL_UNRES_DL_DST] = TRUE;
140 fmt_list[COL_UNRES_NET_DST] = TRUE;
143 fmt_list[COL_RES_DL_SRC] = TRUE;
146 fmt_list[COL_RES_DL_DST] = TRUE;
148 case COL_DEF_NET_SRC:
149 fmt_list[COL_RES_NET_SRC] = TRUE;
151 case COL_DEF_NET_DST:
152 fmt_list[COL_RES_NET_DST] = TRUE;
154 case COL_DEF_SRC_PORT:
155 fmt_list[COL_RES_SRC_PORT] = TRUE;
157 case COL_DEF_DST_PORT:
158 fmt_list[COL_RES_DST_PORT] = TRUE;
166 /* Returns the longest possible width for a particular column type */
167 /* XXX - this is somewhat fragile; we should probably generate */
168 /* the summary lines for all the packets first, and compute the */
169 /* maximum column width as the maximum string width of all the */
170 /* values in that column. */
172 get_column_width(gint format, GdkFont *font) {
175 return (gdk_string_width(font, "0") * 7);
178 return (gdk_string_width(font, "00:00:00.000000"));
182 return (gdk_string_width(font, "0000.000000"));
189 case COL_UNRES_DL_SRC:
190 case COL_DEF_NET_SRC:
191 case COL_RES_NET_SRC:
192 case COL_UNRES_NET_SRC:
198 case COL_UNRES_DL_DST:
199 case COL_DEF_NET_DST:
200 case COL_RES_NET_DST:
201 case COL_UNRES_NET_DST:
202 return (gdk_string_width(font, "00:00:00:00:00:00"));
204 case COL_DEF_SRC_PORT:
205 case COL_RES_SRC_PORT:
206 case COL_UNRES_SRC_PORT:
207 case COL_DEF_DST_PORT:
208 case COL_RES_DST_PORT:
209 case COL_UNRES_DST_PORT:
210 return (gdk_string_width(font, "0") * 6);
213 return (gdk_string_width(font, "NBNS (UDP)"));
215 default: /* COL_INFO */
216 return (gdk_string_width(font, "Source port: kerberos-master "
217 "Destination port: kerberos-master"));
231 get_column_format(gint col) {
232 GList *clp = g_list_nth(prefs.col_list, col);
235 cfmt = (fmt_data *) clp->data;
237 return(get_column_format_from_str(cfmt->fmt));
241 get_column_format_from_str(gchar *str) {
243 gint res_off = RES_DEF, addr_off = ADDR_DEF;
245 /* To do: Make this parse %-formatted strings "for real" */
246 while (*cptr != '\0') {
248 case 't': /* To do: fix for absolute and delta */
255 return COL_DEF_SRC + res_off + addr_off;
258 return COL_DEF_DST + res_off + addr_off;
261 return COL_DEF_SRC_PORT + res_off;
264 return COL_DEF_DST_PORT + res_off;
291 get_column_title(gint col) {
292 GList *clp = g_list_nth(prefs.col_list, col);
295 cfmt = (fmt_data *) clp->data;
300 #define MAX_FMT_PREF_LEN 1024
301 #define MAX_FMT_PREF_LINE_LEN 60
303 col_format_to_pref_str() {
304 static gchar pref_str[MAX_FMT_PREF_LEN] = "";
305 GList *clp = g_list_first(prefs.col_list);
307 int cur_pos = 0, cur_len = 0, fmt_len;
310 cfmt = (fmt_data *) clp->data;
312 fmt_len = strlen(cfmt->title) + 4;
313 if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
314 if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
317 pref_str[cur_len] = '\n'; cur_len++;
318 pref_str[cur_len] = '\t'; cur_len++;
320 sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->title);
325 fmt_len = strlen(cfmt->fmt) + 4;
326 if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
327 if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
330 pref_str[cur_len] = '\n'; cur_len++;
331 pref_str[cur_len] = '\t'; cur_len++;
333 sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->fmt);
342 pref_str[cur_len - 2] = '\0';
347 /* Create and display the column selection widgets. */
348 /* Called when the 'Columns' preference notebook page is selected. */
350 column_prefs_show() {
351 GtkWidget *main_vb, *top_hb, *list_bb, *new_bt, *column_sc, *nl_item,
352 *nl_lb, *tb, *lb, *menu, *mitem, *arrow_hb;
357 /* Container for each row of widgets */
358 main_vb = gtk_vbox_new(FALSE, 5);
359 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
360 gtk_widget_show(main_vb);
361 gtk_object_set_data(GTK_OBJECT(main_vb), E_COL_CM_KEY, (gpointer)FALSE);
363 /* Top row: Column list and buttons */
364 top_hb = gtk_hbox_new(FALSE, 5);
365 gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
366 gtk_widget_show(top_hb);
368 list_bb = gtk_vbutton_box_new();
369 gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
370 gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
371 gtk_widget_show(list_bb);
373 new_bt = gtk_button_new_with_label ("New");
374 gtk_signal_connect(GTK_OBJECT(new_bt), "clicked",
375 GTK_SIGNAL_FUNC(column_sel_new_cb), NULL);
376 gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
377 gtk_widget_show(new_bt);
379 chg_bt = gtk_button_new_with_label ("Change");
380 gtk_widget_set_sensitive(chg_bt, FALSE);
381 gtk_signal_connect(GTK_OBJECT(chg_bt), "clicked",
382 GTK_SIGNAL_FUNC(column_sel_chg_cb), NULL);
383 gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
384 gtk_widget_show(chg_bt);
386 del_bt = gtk_button_new_with_label ("Delete");
387 gtk_widget_set_sensitive(del_bt, FALSE);
388 gtk_signal_connect(GTK_OBJECT(del_bt), "clicked",
389 GTK_SIGNAL_FUNC(column_sel_del_cb), NULL);
390 gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
391 gtk_widget_show(del_bt);
393 arrow_hb = gtk_hbox_new(TRUE, 3);
394 gtk_container_add(GTK_CONTAINER(list_bb), arrow_hb);
395 gtk_widget_show(arrow_hb);
397 up_bt = gtk_button_new_with_label("Up");
398 gtk_widget_set_sensitive(up_bt, FALSE);
399 gtk_signal_connect(GTK_OBJECT(up_bt), "clicked",
400 GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
401 gtk_box_pack_start(GTK_BOX(arrow_hb), up_bt, TRUE, TRUE, 0);
402 gtk_widget_show(up_bt);
404 dn_bt = gtk_button_new_with_label("Down");
405 gtk_widget_set_sensitive(dn_bt, FALSE);
406 gtk_signal_connect(GTK_OBJECT(dn_bt), "clicked",
407 GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
408 gtk_box_pack_start(GTK_BOX(arrow_hb), dn_bt, TRUE, TRUE, 0);
409 gtk_widget_show(dn_bt);
411 column_sc = gtk_scrolled_window_new(NULL, NULL);
412 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(column_sc),
413 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
414 gtk_widget_set_usize(column_sc, 250, 150);
415 gtk_container_add(GTK_CONTAINER(top_hb), column_sc);
416 gtk_widget_show(column_sc);
418 column_l = gtk_list_new();
419 gtk_list_set_selection_mode(GTK_LIST(column_l), GTK_SELECTION_SINGLE);
420 gtk_signal_connect(GTK_OBJECT(column_l), "selection_changed",
421 GTK_SIGNAL_FUNC(column_sel_list_cb), main_vb);
422 #ifdef GTK_HAVE_FEATURES_1_1_4
423 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(column_sc), column_l);
425 gtk_container_add(GTK_CONTAINER(column_sc), column_l);
427 gtk_widget_show(column_l);
429 clp = g_list_first(prefs.col_list);
431 cfmt = (fmt_data *) clp->data;
432 nl_lb = gtk_label_new(cfmt->title);
433 nl_item = gtk_list_item_new();
434 gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
435 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
436 gtk_widget_show(nl_lb);
437 gtk_container_add(GTK_CONTAINER(column_l), nl_item);
438 gtk_widget_show(nl_item);
439 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
440 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY, clp);
445 /* Colunm name entry and format selection */
446 tb = gtk_table_new(2, 2, FALSE);
447 gtk_container_add(GTK_CONTAINER(main_vb), tb);
448 gtk_table_set_row_spacings(GTK_TABLE(tb), 10);
449 gtk_table_set_col_spacings(GTK_TABLE(tb), 15);
452 lb = gtk_label_new("Column title:");
453 gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
454 gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 0, 1);
457 title_te = gtk_entry_new();
458 gtk_table_attach_defaults(GTK_TABLE(tb), title_te, 1, 2, 0, 1);
459 gtk_widget_show(title_te);
461 lb = gtk_label_new("Column format:");
462 gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
463 gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 1, 2);
466 fmt_m = gtk_option_menu_new();
467 menu = gtk_menu_new();
468 for (i = 0; i < NUM_COL_FMTS; i++) {
469 mitem = gtk_menu_item_new_with_label(col_format_desc(i));
470 gtk_menu_append(GTK_MENU(menu), mitem);
471 gtk_signal_connect_object( GTK_OBJECT(mitem), "activate",
472 GTK_SIGNAL_FUNC(column_set_fmt_cb), (gpointer) i);
473 gtk_widget_show(mitem);
475 gtk_option_menu_set_menu(GTK_OPTION_MENU(fmt_m), menu);
477 gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
478 gtk_table_attach_defaults(GTK_TABLE(tb), fmt_m, 1, 2, 1, 2);
479 gtk_widget_show(fmt_m);
485 column_sel_list_cb(GtkWidget *l, gpointer data) {
490 gint sensitivity = FALSE, up_sens = FALSE, dn_sens = FALSE;
492 sl = GTK_LIST(l)->selection;
494 if (sl) { /* Something was selected */
495 l_item = GTK_OBJECT(sl->data);
496 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
498 cfmt = (fmt_data *) clp->data;
500 cur_fmt = get_column_format_from_str(cfmt->fmt);
501 gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
503 if (clp != g_list_first(prefs.col_list))
505 if (clp != g_list_last(prefs.col_list))
510 /* Did you know that this function is called when the window is destroyed? */
512 if (!gtk_object_get_data(GTK_OBJECT(data), E_COL_CM_KEY)) {
513 gtk_entry_set_text(GTK_ENTRY(title_te), title);
514 gtk_widget_set_sensitive(chg_bt, sensitivity);
515 gtk_widget_set_sensitive(del_bt, sensitivity);
516 gtk_widget_set_sensitive(up_bt, up_sens);
517 gtk_widget_set_sensitive(dn_bt, dn_sens);
521 /* To do: add input checking to each of these callbacks */
524 column_sel_new_cb(GtkWidget *w, gpointer data) {
527 GtkWidget *nl_item, *nl_lb;
529 title = gtk_entry_get_text(GTK_ENTRY(title_te));
531 if (strlen(title) > 0) {
532 cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
533 cfmt->title = g_strdup(title);
534 cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
535 prefs.col_list = g_list_append(prefs.col_list, cfmt);
536 nl_lb = gtk_label_new(cfmt->title);
537 nl_item = gtk_list_item_new();
538 gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
539 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
540 gtk_widget_show(nl_lb);
541 gtk_container_add(GTK_CONTAINER(column_l), nl_item);
542 gtk_widget_show(nl_item);
543 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
544 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY,
545 g_list_last(prefs.col_list));
546 gtk_list_select_child(GTK_LIST(column_l), nl_item);
551 column_sel_chg_cb(GtkWidget *w, gpointer data) {
558 sl = GTK_LIST(column_l)->selection;
559 title = gtk_entry_get_text(GTK_ENTRY(title_te));
561 if (sl) { /* Something was selected */
562 l_item = GTK_OBJECT(sl->data);
563 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
564 nl_lb = (GtkLabel *) gtk_object_get_data(l_item, E_COL_LBL_KEY);
566 cfmt = (fmt_data *) clp->data;
568 if (strlen(title) > 0 && cfmt) {
571 cfmt->title = g_strdup(title);
572 cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
573 gtk_label_set(nl_lb, cfmt->title);
580 column_sel_del_cb(GtkWidget *w, gpointer data) {
586 sl = GTK_LIST(column_l)->selection;
587 if (sl) { /* Something was selected */
588 l_item = GTK_OBJECT(sl->data);
589 pos = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
590 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
592 cfmt = (fmt_data *) clp->data;
596 prefs.col_list = g_list_remove_link(prefs.col_list, clp);
597 gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
603 column_sel_arrow_cb(GtkWidget *w, gpointer data) {
604 GList *sl, *clp, *il;
612 sl = GTK_LIST(column_l)->selection;
613 if (sl) { /* Something was selected */
614 l_item = GTK_OBJECT(sl->data);
615 pos = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
616 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
618 cfmt = (fmt_data *) clp->data;
619 prefs.col_list = g_list_remove(prefs.col_list, cfmt);
620 g_list_insert(prefs.col_list, cfmt, pos + inc);
621 il = (GList *) g_malloc(sizeof(GList));
625 gtk_widget_ref(GTK_WIDGET(l_item));
626 gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
627 gtk_list_insert_items(GTK_LIST(column_l), il, pos + inc);
628 gtk_widget_unref(GTK_WIDGET(l_item));
629 gtk_list_select_item(GTK_LIST(column_l), pos + inc);
635 column_set_fmt_cb(GtkWidget *w, gpointer data) {
636 cur_fmt = (gint) data;
640 column_prefs_ok(GtkWidget *w) {
642 column_prefs_cancel(w);
646 column_prefs_save(GtkWidget *w) {
650 column_prefs_cancel(GtkWidget *w) {
652 /* Let the list cb know we're about to destroy the widget tree, so it */
653 /* doesn't operate on widgets that don't exist. */
654 gtk_object_set_data(GTK_OBJECT(w), E_COL_CM_KEY, (gpointer)TRUE);
655 gtk_widget_destroy(GTK_WIDGET(w));