2 * Routines for handling column preferences
4 * $Id: column.c,v 1.10 1999/03/23 03:14:32 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>
49 static GtkWidget *column_l, *chg_bt, *del_bt, *title_te, *fmt_m, *up_bt,
53 #define E_COL_NAME_KEY "column_name"
54 #define E_COL_LBL_KEY "column_label"
55 #define E_COL_CM_KEY "in_col_cancel_mode"
57 static gchar *col_format_to_string(gint);
58 static gchar *col_format_desc(gint);
59 static gint get_column_format_from_str(gchar *str);
60 static void column_sel_list_cb(GtkWidget *, gpointer);
61 static void column_sel_new_cb(GtkWidget *, gpointer);
62 static void column_sel_chg_cb(GtkWidget *, gpointer);
63 static void column_sel_del_cb(GtkWidget *, gpointer);
64 static void column_sel_arrow_cb(GtkWidget *, gpointer);
65 static void column_set_fmt_cb(GtkWidget *, gpointer);
67 /* Given a format number (as defined in ethereal.h), returns its equivalent
70 col_format_to_string(gint fmt) {
71 gchar *slist[] = { "%m", "%t", "%Rt", "%At", "%Tt", "%s", "%rs", "%us",
72 "%hs", "%rhs", "%uhs", "%ns", "%rns", "%uns", "%d",
73 "%rd", "%ud", "%hd", "%rhd", "%uhd", "%nd", "%rnd",
74 "%und", "%S", "%rS", "%uS", "%D", "%rD", "%uD", "%p",
77 if (fmt < 0 || fmt > NUM_COL_FMTS)
83 /* Given a format number (as defined in ethereal.h), returns its
86 col_format_desc(gint fmt) {
87 gchar *dlist[] = { "Number", "Time (command line specified)",
88 "Relative time", "Absolute time", "Delta time",
89 "Source address", "Src addr (resolved)",
90 "Src addr (unresolved)", "Hardware src addr",
91 "Hw src addr (resolved)", "Hw src addr (unresolved)",
92 "Network src addr", "Net scr addr (resolved)",
93 "Net src addr (unresolved)", "Destination address",
94 "Dest addr (resolved)", "Dest addr (unresolved)",
95 "Hardware dest addr", "Hw dest addr (resolved)",
96 "Hw dest addr (unresolved)", "Network dest addr",
97 "Net dest addr (resolved)", "Net dest addr (unresolved)",
98 "Source port", "Src port (resolved)",
99 "Src port (unresolved)", "Destination port",
100 "Dest port (resolved)", "Dest port (unresolved)",
101 "Protocol", "Information" };
103 if (fmt < 0 || fmt > NUM_COL_FMTS)
109 /* Marks each array element true if it can be substituted for the given
112 get_column_format_matches(gboolean *fmt_list, gint format) {
115 for (i = 0; i < NUM_COL_FMTS; i++) {
116 /* Get the obvious: the format itself */
119 /* Get any formats lower down on the chain */
122 switch (timestamp_type) {
124 fmt_list[COL_ABS_TIME] = TRUE;
127 fmt_list[COL_DELTA_TIME] = TRUE;
130 fmt_list[COL_REL_TIME] = TRUE;
135 fmt_list[COL_RES_DL_SRC] = TRUE;
136 fmt_list[COL_RES_NET_SRC] = TRUE;
139 fmt_list[COL_RES_DL_SRC] = TRUE;
140 fmt_list[COL_RES_NET_SRC] = TRUE;
143 fmt_list[COL_UNRES_DL_SRC] = TRUE;
144 fmt_list[COL_UNRES_NET_SRC] = TRUE;
147 fmt_list[COL_RES_DL_DST] = TRUE;
148 fmt_list[COL_RES_NET_DST] = TRUE;
151 fmt_list[COL_RES_DL_DST] = TRUE;
152 fmt_list[COL_RES_NET_DST] = TRUE;
155 fmt_list[COL_UNRES_DL_DST] = TRUE;
156 fmt_list[COL_UNRES_NET_DST] = TRUE;
159 fmt_list[COL_RES_DL_SRC] = TRUE;
162 fmt_list[COL_RES_DL_DST] = TRUE;
164 case COL_DEF_NET_SRC:
165 fmt_list[COL_RES_NET_SRC] = TRUE;
167 case COL_DEF_NET_DST:
168 fmt_list[COL_RES_NET_DST] = TRUE;
170 case COL_DEF_SRC_PORT:
171 fmt_list[COL_RES_SRC_PORT] = TRUE;
173 case COL_DEF_DST_PORT:
174 fmt_list[COL_RES_DST_PORT] = TRUE;
182 /* Returns the longest possible width for a particular column type */
183 /* XXX - this is somewhat fragile; we should probably generate */
184 /* the summary lines for all the packets first, and compute the */
185 /* maximum column width as the maximum string width of all the */
186 /* values in that column. */
188 get_column_width(gint format, GdkFont *font) {
191 return (gdk_string_width(font, "0") * 7);
194 if (timestamp_type == COL_ABS_TIME)
195 return (gdk_string_width(font, "00:00:00.000000"));
197 return (gdk_string_width(font, "0000.000000"));
200 return (gdk_string_width(font, "00:00:00.000000"));
204 return (gdk_string_width(font, "0000.000000"));
211 case COL_UNRES_DL_SRC:
212 case COL_DEF_NET_SRC:
213 case COL_RES_NET_SRC:
214 case COL_UNRES_NET_SRC:
220 case COL_UNRES_DL_DST:
221 case COL_DEF_NET_DST:
222 case COL_RES_NET_DST:
223 case COL_UNRES_NET_DST:
224 return (gdk_string_width(font, "00000000.000000000000")); /* IPX-style */
226 case COL_DEF_SRC_PORT:
227 case COL_RES_SRC_PORT:
228 case COL_UNRES_SRC_PORT:
229 case COL_DEF_DST_PORT:
230 case COL_RES_DST_PORT:
231 case COL_UNRES_DST_PORT:
232 return (gdk_string_width(font, "0") * 6);
235 return (gdk_string_width(font, "NBNS (UDP)"));
237 default: /* COL_INFO */
238 return (gdk_string_width(font, "Source port: kerberos-master "
239 "Destination port: kerberos-master"));
258 get_column_format(gint col) {
259 GList *clp = g_list_nth(prefs.col_list, col);
262 cfmt = (fmt_data *) clp->data;
264 return(get_column_format_from_str(cfmt->fmt));
268 get_column_format_from_str(gchar *str) {
270 gint res_off = RES_DEF, addr_off = ADDR_DEF, time_off = TIME_DEF;
272 /* To do: Make this parse %-formatted strings "for real" */
273 while (*cptr != '\0') {
275 case 't': /* To do: fix for absolute and delta */
276 return COL_CLS_TIME + time_off;
282 return COL_DEF_SRC + res_off + addr_off;
285 return COL_DEF_DST + res_off + addr_off;
288 return COL_DEF_SRC_PORT + res_off;
291 return COL_DEF_DST_PORT + res_off;
327 get_column_title(gint col) {
328 GList *clp = g_list_nth(prefs.col_list, col);
331 cfmt = (fmt_data *) clp->data;
336 #define MAX_FMT_PREF_LEN 1024
337 #define MAX_FMT_PREF_LINE_LEN 60
339 col_format_to_pref_str() {
340 static gchar pref_str[MAX_FMT_PREF_LEN] = "";
341 GList *clp = g_list_first(prefs.col_list);
343 int cur_pos = 0, cur_len = 0, fmt_len;
346 cfmt = (fmt_data *) clp->data;
348 fmt_len = strlen(cfmt->title) + 4;
349 if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
350 if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
353 pref_str[cur_len] = '\n'; cur_len++;
354 pref_str[cur_len] = '\t'; cur_len++;
356 sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->title);
361 fmt_len = strlen(cfmt->fmt) + 4;
362 if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
363 if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
366 pref_str[cur_len] = '\n'; cur_len++;
367 pref_str[cur_len] = '\t'; cur_len++;
369 sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->fmt);
378 pref_str[cur_len - 2] = '\0';
383 /* Create and display the column selection widgets. */
384 /* Called when the 'Columns' preference notebook page is selected. */
386 column_prefs_show() {
387 GtkWidget *main_vb, *top_hb, *list_bb, *new_bt, *column_sc, *nl_item,
388 *nl_lb, *tb, *lb, *menu, *mitem, *arrow_hb;
393 /* Container for each row of widgets */
394 main_vb = gtk_vbox_new(FALSE, 5);
395 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
396 gtk_widget_show(main_vb);
397 gtk_object_set_data(GTK_OBJECT(main_vb), E_COL_CM_KEY, (gpointer)FALSE);
399 /* Top row: Column list and buttons */
400 top_hb = gtk_hbox_new(FALSE, 5);
401 gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
402 gtk_widget_show(top_hb);
404 list_bb = gtk_vbutton_box_new();
405 gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
406 gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
407 gtk_widget_show(list_bb);
409 new_bt = gtk_button_new_with_label ("New");
410 gtk_signal_connect(GTK_OBJECT(new_bt), "clicked",
411 GTK_SIGNAL_FUNC(column_sel_new_cb), NULL);
412 gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
413 gtk_widget_show(new_bt);
415 chg_bt = gtk_button_new_with_label ("Change");
416 gtk_widget_set_sensitive(chg_bt, FALSE);
417 gtk_signal_connect(GTK_OBJECT(chg_bt), "clicked",
418 GTK_SIGNAL_FUNC(column_sel_chg_cb), NULL);
419 gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
420 gtk_widget_show(chg_bt);
422 del_bt = gtk_button_new_with_label ("Delete");
423 gtk_widget_set_sensitive(del_bt, FALSE);
424 gtk_signal_connect(GTK_OBJECT(del_bt), "clicked",
425 GTK_SIGNAL_FUNC(column_sel_del_cb), NULL);
426 gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
427 gtk_widget_show(del_bt);
429 arrow_hb = gtk_hbox_new(TRUE, 3);
430 gtk_container_add(GTK_CONTAINER(list_bb), arrow_hb);
431 gtk_widget_show(arrow_hb);
433 up_bt = gtk_button_new_with_label("Up");
434 gtk_widget_set_sensitive(up_bt, FALSE);
435 gtk_signal_connect(GTK_OBJECT(up_bt), "clicked",
436 GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
437 gtk_box_pack_start(GTK_BOX(arrow_hb), up_bt, TRUE, TRUE, 0);
438 gtk_widget_show(up_bt);
440 dn_bt = gtk_button_new_with_label("Down");
441 gtk_widget_set_sensitive(dn_bt, FALSE);
442 gtk_signal_connect(GTK_OBJECT(dn_bt), "clicked",
443 GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
444 gtk_box_pack_start(GTK_BOX(arrow_hb), dn_bt, TRUE, TRUE, 0);
445 gtk_widget_show(dn_bt);
447 column_sc = gtk_scrolled_window_new(NULL, NULL);
448 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(column_sc),
449 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
450 gtk_widget_set_usize(column_sc, 250, 150);
451 gtk_container_add(GTK_CONTAINER(top_hb), column_sc);
452 gtk_widget_show(column_sc);
454 column_l = gtk_list_new();
455 gtk_list_set_selection_mode(GTK_LIST(column_l), GTK_SELECTION_SINGLE);
456 gtk_signal_connect(GTK_OBJECT(column_l), "selection_changed",
457 GTK_SIGNAL_FUNC(column_sel_list_cb), main_vb);
458 #ifdef GTK_HAVE_FEATURES_1_1_4
459 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(column_sc), column_l);
461 gtk_container_add(GTK_CONTAINER(column_sc), column_l);
463 gtk_widget_show(column_l);
465 clp = g_list_first(prefs.col_list);
467 cfmt = (fmt_data *) clp->data;
468 nl_lb = gtk_label_new(cfmt->title);
469 nl_item = gtk_list_item_new();
470 gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
471 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
472 gtk_widget_show(nl_lb);
473 gtk_container_add(GTK_CONTAINER(column_l), nl_item);
474 gtk_widget_show(nl_item);
475 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
476 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY, clp);
481 /* Colunm name entry and format selection */
482 tb = gtk_table_new(2, 2, FALSE);
483 gtk_container_add(GTK_CONTAINER(main_vb), tb);
484 gtk_table_set_row_spacings(GTK_TABLE(tb), 10);
485 gtk_table_set_col_spacings(GTK_TABLE(tb), 15);
488 lb = gtk_label_new("Column title:");
489 gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
490 gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 0, 1);
493 title_te = gtk_entry_new();
494 gtk_table_attach_defaults(GTK_TABLE(tb), title_te, 1, 2, 0, 1);
495 gtk_widget_show(title_te);
497 lb = gtk_label_new("Column format:");
498 gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
499 gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 1, 2);
502 fmt_m = gtk_option_menu_new();
503 menu = gtk_menu_new();
504 for (i = 0; i < NUM_COL_FMTS; i++) {
505 mitem = gtk_menu_item_new_with_label(col_format_desc(i));
506 gtk_menu_append(GTK_MENU(menu), mitem);
507 gtk_signal_connect_object( GTK_OBJECT(mitem), "activate",
508 GTK_SIGNAL_FUNC(column_set_fmt_cb), (gpointer) i);
509 gtk_widget_show(mitem);
511 gtk_option_menu_set_menu(GTK_OPTION_MENU(fmt_m), menu);
513 gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
514 gtk_table_attach_defaults(GTK_TABLE(tb), fmt_m, 1, 2, 1, 2);
515 gtk_widget_show(fmt_m);
521 column_sel_list_cb(GtkWidget *l, gpointer data) {
526 gint sensitivity = FALSE, up_sens = FALSE, dn_sens = FALSE;
528 sl = GTK_LIST(l)->selection;
530 if (sl) { /* Something was selected */
531 l_item = GTK_OBJECT(sl->data);
532 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
534 cfmt = (fmt_data *) clp->data;
536 cur_fmt = get_column_format_from_str(cfmt->fmt);
537 gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
539 if (clp != g_list_first(prefs.col_list))
541 if (clp != g_list_last(prefs.col_list))
546 /* Did you know that this function is called when the window is destroyed? */
548 if (!gtk_object_get_data(GTK_OBJECT(data), E_COL_CM_KEY)) {
549 gtk_entry_set_text(GTK_ENTRY(title_te), title);
550 gtk_widget_set_sensitive(chg_bt, sensitivity);
551 gtk_widget_set_sensitive(del_bt, sensitivity);
552 gtk_widget_set_sensitive(up_bt, up_sens);
553 gtk_widget_set_sensitive(dn_bt, dn_sens);
557 /* To do: add input checking to each of these callbacks */
560 column_sel_new_cb(GtkWidget *w, gpointer data) {
563 GtkWidget *nl_item, *nl_lb;
565 title = gtk_entry_get_text(GTK_ENTRY(title_te));
567 if (strlen(title) > 0) {
568 cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
569 cfmt->title = g_strdup(title);
570 cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
571 prefs.col_list = g_list_append(prefs.col_list, cfmt);
572 nl_lb = gtk_label_new(cfmt->title);
573 nl_item = gtk_list_item_new();
574 gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
575 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
576 gtk_widget_show(nl_lb);
577 gtk_container_add(GTK_CONTAINER(column_l), nl_item);
578 gtk_widget_show(nl_item);
579 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
580 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY,
581 g_list_last(prefs.col_list));
582 gtk_list_select_child(GTK_LIST(column_l), nl_item);
587 column_sel_chg_cb(GtkWidget *w, gpointer data) {
594 sl = GTK_LIST(column_l)->selection;
595 title = gtk_entry_get_text(GTK_ENTRY(title_te));
597 if (sl) { /* Something was selected */
598 l_item = GTK_OBJECT(sl->data);
599 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
600 nl_lb = (GtkLabel *) gtk_object_get_data(l_item, E_COL_LBL_KEY);
602 cfmt = (fmt_data *) clp->data;
604 if (strlen(title) > 0 && cfmt) {
607 cfmt->title = g_strdup(title);
608 cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
609 gtk_label_set(nl_lb, cfmt->title);
616 column_sel_del_cb(GtkWidget *w, gpointer data) {
622 sl = GTK_LIST(column_l)->selection;
623 if (sl) { /* Something was selected */
624 l_item = GTK_OBJECT(sl->data);
625 pos = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
626 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
628 cfmt = (fmt_data *) clp->data;
632 prefs.col_list = g_list_remove_link(prefs.col_list, clp);
633 gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
639 column_sel_arrow_cb(GtkWidget *w, gpointer data) {
640 GList *sl, *clp, *il;
648 sl = GTK_LIST(column_l)->selection;
649 if (sl) { /* Something was selected */
650 l_item = GTK_OBJECT(sl->data);
651 pos = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
652 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
654 cfmt = (fmt_data *) clp->data;
655 prefs.col_list = g_list_remove(prefs.col_list, cfmt);
656 g_list_insert(prefs.col_list, cfmt, pos + inc);
657 il = (GList *) g_malloc(sizeof(GList));
661 gtk_widget_ref(GTK_WIDGET(l_item));
662 gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
663 gtk_list_insert_items(GTK_LIST(column_l), il, pos + inc);
664 gtk_widget_unref(GTK_WIDGET(l_item));
665 gtk_list_select_item(GTK_LIST(column_l), pos + inc);
671 column_set_fmt_cb(GtkWidget *w, gpointer data) {
672 cur_fmt = (gint) data;
676 column_prefs_ok(GtkWidget *w) {
678 column_prefs_cancel(w);
682 column_prefs_save(GtkWidget *w) {
686 column_prefs_cancel(GtkWidget *w) {
688 /* Let the list cb know we're about to destroy the widget tree, so it */
689 /* doesn't operate on widgets that don't exist. */
690 gtk_object_set_data(GTK_OBJECT(w), E_COL_CM_KEY, (gpointer)TRUE);
691 gtk_widget_destroy(GTK_WIDGET(w));