2 * Routines for handling column preferences
4 * $Id: column.c,v 1.26 1999/12/29 10:48:01 guy 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>
46 #include "timestamp.h"
51 static GtkWidget *column_l, *chg_bt, *del_bt, *title_te, *fmt_m, *up_bt,
55 #define E_COL_NAME_KEY "column_name"
56 #define E_COL_LBL_KEY "column_label"
57 #define E_COL_CM_KEY "in_col_cancel_mode"
59 static gchar *col_format_to_string(gint);
60 static gchar *col_format_desc(gint);
61 static gint get_column_format_from_str(gchar *str);
62 static void column_sel_list_cb(GtkWidget *, gpointer);
63 static void column_sel_new_cb(GtkWidget *, gpointer);
64 static void column_sel_chg_cb(GtkWidget *, gpointer);
65 static void column_sel_del_cb(GtkWidget *, gpointer);
66 static void column_sel_arrow_cb(GtkWidget *, gpointer);
67 static void column_set_fmt_cb(GtkWidget *, gpointer);
69 /* Given a format number (as defined in packet.h), returns its equivalent
72 col_format_to_string(gint fmt) {
73 gchar *slist[] = { "%m", "%t", "%Rt", "%At", "%Tt", "%s", "%rs", "%us",
74 "%hs", "%rhs", "%uhs", "%ns", "%rns", "%uns", "%d",
75 "%rd", "%ud", "%hd", "%rhd", "%uhd", "%nd", "%rnd",
76 "%und", "%S", "%rS", "%uS", "%D", "%rD", "%uD", "%p",
79 if (fmt < 0 || fmt > NUM_COL_FMTS)
85 /* Given a format number (as defined in packet.h), returns its
88 col_format_desc(gint fmt) {
89 gchar *dlist[] = { "Number", "Time (command line specified)",
90 "Relative time", "Absolute time", "Delta time",
91 "Source address", "Src addr (resolved)",
92 "Src addr (unresolved)", "Hardware src addr",
93 "Hw src addr (resolved)", "Hw src addr (unresolved)",
94 "Network src addr", "Net src addr (resolved)",
95 "Net src addr (unresolved)", "Destination address",
96 "Dest addr (resolved)", "Dest addr (unresolved)",
97 "Hardware dest addr", "Hw dest addr (resolved)",
98 "Hw dest addr (unresolved)", "Network dest addr",
99 "Net dest addr (resolved)", "Net dest addr (unresolved)",
100 "Source port", "Src port (resolved)",
101 "Src port (unresolved)", "Destination port",
102 "Dest port (resolved)", "Dest port (unresolved)",
103 "Protocol", "Information", "Packet length (bytes)" };
105 if (fmt < 0 || fmt > NUM_COL_FMTS)
111 /* Marks each array element true if it can be substituted for the given
114 get_column_format_matches(gboolean *fmt_list, gint format) {
117 for (i = 0; i < NUM_COL_FMTS; i++) {
118 /* Get the obvious: the format itself */
121 /* Get any formats lower down on the chain */
124 fmt_list[COL_RES_DL_SRC] = TRUE;
125 fmt_list[COL_RES_NET_SRC] = TRUE;
128 fmt_list[COL_RES_DL_SRC] = TRUE;
129 fmt_list[COL_RES_NET_SRC] = TRUE;
132 fmt_list[COL_UNRES_DL_SRC] = TRUE;
133 fmt_list[COL_UNRES_NET_SRC] = TRUE;
136 fmt_list[COL_RES_DL_DST] = TRUE;
137 fmt_list[COL_RES_NET_DST] = TRUE;
140 fmt_list[COL_RES_DL_DST] = TRUE;
141 fmt_list[COL_RES_NET_DST] = TRUE;
144 fmt_list[COL_UNRES_DL_DST] = TRUE;
145 fmt_list[COL_UNRES_NET_DST] = TRUE;
148 fmt_list[COL_RES_DL_SRC] = TRUE;
151 fmt_list[COL_RES_DL_DST] = TRUE;
153 case COL_DEF_NET_SRC:
154 fmt_list[COL_RES_NET_SRC] = TRUE;
156 case COL_DEF_NET_DST:
157 fmt_list[COL_RES_NET_DST] = TRUE;
159 case COL_DEF_SRC_PORT:
160 fmt_list[COL_RES_SRC_PORT] = TRUE;
162 case COL_DEF_DST_PORT:
163 fmt_list[COL_RES_DST_PORT] = TRUE;
171 /* Returns a string representing the longest possible value for a
172 particular column type. */
174 get_column_longest_string(gint format)
181 if (timestamp_type == ABSOLUTE)
182 return "00:00:00.000000";
184 return "0000.000000";
187 return "00:00:00.000000";
191 return "0000.000000";
198 case COL_UNRES_DL_SRC:
199 case COL_DEF_NET_SRC:
200 case COL_RES_NET_SRC:
201 case COL_UNRES_NET_SRC:
207 case COL_UNRES_DL_DST:
208 case COL_DEF_NET_DST:
209 case COL_RES_NET_DST:
210 case COL_UNRES_NET_DST:
211 return "00000000.000000000000"; /* IPX-style */
213 case COL_DEF_SRC_PORT:
214 case COL_RES_SRC_PORT:
215 case COL_UNRES_SRC_PORT:
216 case COL_DEF_DST_PORT:
217 case COL_RES_DST_PORT:
218 case COL_UNRES_DST_PORT:
222 return "NetBIOS"; /* not the longest, but the longest is too long */
224 case COL_PACKET_LENGTH:
227 default: /* COL_INFO */
228 return "Source port: kerberos-master Destination port: kerberos-master";
233 /* Returns the longest possible width, using the specified font,
234 for a particular column type.
236 Except for the COL...SRC and COL...DST columns, these are used
237 only when a capture is being displayed while it's taking place;
238 they are arguably somewhat fragile, as changes to the code that
239 generates them don't cause these widths to change, but that's
240 probably not too big a problem, given that the sizes are
241 recomputed based on the actual data in the columns when the capture
242 is done, and given that the width for COL...SRC and COL...DST columns
243 is somewhat arbitrary in any case. We should probably clean
244 that up eventually, though. */
246 get_column_width(gint format, GdkFont *font)
248 return (gdk_string_width(font, get_column_longest_string(format)));
251 /* Returns the longest possible width, in characters, for a particular
254 get_column_char_width(gint format)
256 return strlen(get_column_longest_string(format));
260 get_column_resize_type(gint format) {
267 case COL_DEF_SRC_PORT:
268 case COL_RES_SRC_PORT:
269 case COL_UNRES_SRC_PORT:
270 case COL_DEF_DST_PORT:
271 case COL_RES_DST_PORT:
272 case COL_UNRES_DST_PORT:
274 case COL_PACKET_LENGTH:
275 /* We don't want these to resize during a live capture, as that
276 gets in the way of trying to look at the data while it's being
278 return (RESIZE_AUTO);
285 case COL_UNRES_DL_SRC:
286 case COL_DEF_NET_SRC:
287 case COL_RES_NET_SRC:
288 case COL_UNRES_NET_SRC:
294 case COL_UNRES_DL_DST:
295 case COL_DEF_NET_DST:
296 case COL_RES_NET_DST:
297 case COL_UNRES_NET_DST:
298 /* We don't want these to resize dynamically; if they get resolved
299 to names, those names could be very long, and auto-resizing
300 columns showing those names may leave too little room for
301 other columns such as the "Info" column. */
302 return (RESIZE_MANUAL);
304 default: /* COL_INFO */
305 /* We want this to resize dynamically, even during a live capture,
306 because otherewise you won't be able to see all that's in
308 return (RESIZE_LIVE);
327 get_column_format(gint col) {
328 GList *clp = g_list_nth(prefs.col_list, col);
331 cfmt = (fmt_data *) clp->data;
333 return(get_column_format_from_str(cfmt->fmt));
337 get_column_format_from_str(gchar *str) {
339 gint res_off = RES_DEF, addr_off = ADDR_DEF, time_off = TIME_DEF;
341 /* To do: Make this parse %-formatted strings "for real" */
342 while (*cptr != '\0') {
344 case 't': /* To do: fix for absolute and delta */
345 return COL_CLS_TIME + time_off;
351 return COL_DEF_SRC + res_off + addr_off;
354 return COL_DEF_DST + res_off + addr_off;
357 return COL_DEF_SRC_PORT + res_off;
360 return COL_DEF_DST_PORT + res_off;
390 return COL_PACKET_LENGTH;
399 get_column_title(gint col) {
400 GList *clp = g_list_nth(prefs.col_list, col);
403 cfmt = (fmt_data *) clp->data;
408 #define MAX_FMT_PREF_LEN 1024
409 #define MAX_FMT_PREF_LINE_LEN 60
411 col_format_to_pref_str() {
412 static gchar pref_str[MAX_FMT_PREF_LEN] = "";
413 GList *clp = g_list_first(prefs.col_list);
415 int cur_pos = 0, cur_len = 0, fmt_len;
418 cfmt = (fmt_data *) clp->data;
420 fmt_len = strlen(cfmt->title) + 4;
421 if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
422 if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
425 pref_str[cur_len] = '\n'; cur_len++;
426 pref_str[cur_len] = '\t'; cur_len++;
428 sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->title);
433 fmt_len = strlen(cfmt->fmt) + 4;
434 if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
435 if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
438 pref_str[cur_len] = '\n'; cur_len++;
439 pref_str[cur_len] = '\t'; cur_len++;
441 sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->fmt);
450 pref_str[cur_len - 2] = '\0';
455 /* Create and display the column selection widgets. */
456 /* Called when the 'Columns' preference notebook page is selected. */
458 column_prefs_show() {
459 GtkWidget *main_vb, *top_hb, *list_bb, *new_bt, *column_sc, *nl_item,
460 *nl_lb, *tb, *lb, *menu, *mitem, *arrow_hb;
465 /* Container for each row of widgets */
466 main_vb = gtk_vbox_new(FALSE, 5);
467 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
468 gtk_widget_show(main_vb);
469 gtk_object_set_data(GTK_OBJECT(main_vb), E_COL_CM_KEY, (gpointer)FALSE);
471 /* Top row: Column list and buttons */
472 top_hb = gtk_hbox_new(FALSE, 5);
473 gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
474 gtk_widget_show(top_hb);
476 list_bb = gtk_vbutton_box_new();
477 gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
478 gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
479 gtk_widget_show(list_bb);
481 new_bt = gtk_button_new_with_label ("New");
482 gtk_signal_connect(GTK_OBJECT(new_bt), "clicked",
483 GTK_SIGNAL_FUNC(column_sel_new_cb), NULL);
484 gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
485 gtk_widget_show(new_bt);
487 chg_bt = gtk_button_new_with_label ("Change");
488 gtk_widget_set_sensitive(chg_bt, FALSE);
489 gtk_signal_connect(GTK_OBJECT(chg_bt), "clicked",
490 GTK_SIGNAL_FUNC(column_sel_chg_cb), NULL);
491 gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
492 gtk_widget_show(chg_bt);
494 del_bt = gtk_button_new_with_label ("Delete");
495 gtk_widget_set_sensitive(del_bt, FALSE);
496 gtk_signal_connect(GTK_OBJECT(del_bt), "clicked",
497 GTK_SIGNAL_FUNC(column_sel_del_cb), NULL);
498 gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
499 gtk_widget_show(del_bt);
501 arrow_hb = gtk_hbox_new(TRUE, 3);
502 gtk_container_add(GTK_CONTAINER(list_bb), arrow_hb);
503 gtk_widget_show(arrow_hb);
505 up_bt = gtk_button_new_with_label("Up");
506 gtk_widget_set_sensitive(up_bt, FALSE);
507 gtk_signal_connect(GTK_OBJECT(up_bt), "clicked",
508 GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
509 gtk_box_pack_start(GTK_BOX(arrow_hb), up_bt, TRUE, TRUE, 0);
510 gtk_widget_show(up_bt);
512 dn_bt = gtk_button_new_with_label("Down");
513 gtk_widget_set_sensitive(dn_bt, FALSE);
514 gtk_signal_connect(GTK_OBJECT(dn_bt), "clicked",
515 GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
516 gtk_box_pack_start(GTK_BOX(arrow_hb), dn_bt, TRUE, TRUE, 0);
517 gtk_widget_show(dn_bt);
519 column_sc = gtk_scrolled_window_new(NULL, NULL);
520 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(column_sc),
521 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
522 gtk_widget_set_usize(column_sc, 250, 150);
523 gtk_container_add(GTK_CONTAINER(top_hb), column_sc);
524 gtk_widget_show(column_sc);
526 column_l = gtk_list_new();
527 gtk_list_set_selection_mode(GTK_LIST(column_l), GTK_SELECTION_SINGLE);
528 gtk_signal_connect(GTK_OBJECT(column_l), "selection_changed",
529 GTK_SIGNAL_FUNC(column_sel_list_cb), main_vb);
530 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(column_sc), column_l);
531 gtk_widget_show(column_l);
533 clp = g_list_first(prefs.col_list);
535 cfmt = (fmt_data *) clp->data;
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, clp);
549 /* Colunm name entry and format selection */
550 tb = gtk_table_new(2, 2, FALSE);
551 gtk_container_add(GTK_CONTAINER(main_vb), tb);
552 gtk_table_set_row_spacings(GTK_TABLE(tb), 10);
553 gtk_table_set_col_spacings(GTK_TABLE(tb), 15);
556 lb = gtk_label_new("Column title:");
557 gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
558 gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 0, 1);
561 title_te = gtk_entry_new();
562 gtk_table_attach_defaults(GTK_TABLE(tb), title_te, 1, 2, 0, 1);
563 gtk_widget_show(title_te);
565 lb = gtk_label_new("Column format:");
566 gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
567 gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 1, 2);
570 fmt_m = gtk_option_menu_new();
571 menu = gtk_menu_new();
572 for (i = 0; i < NUM_COL_FMTS; i++) {
573 mitem = gtk_menu_item_new_with_label(col_format_desc(i));
574 gtk_menu_append(GTK_MENU(menu), mitem);
575 gtk_signal_connect( GTK_OBJECT(mitem), "activate",
576 GTK_SIGNAL_FUNC(column_set_fmt_cb), (gpointer) i);
577 gtk_widget_show(mitem);
579 gtk_option_menu_set_menu(GTK_OPTION_MENU(fmt_m), menu);
581 gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
582 gtk_table_attach_defaults(GTK_TABLE(tb), fmt_m, 1, 2, 1, 2);
583 gtk_widget_show(fmt_m);
589 column_sel_list_cb(GtkWidget *l, gpointer data) {
594 gint sensitivity = FALSE, up_sens = FALSE, dn_sens = FALSE;
596 sl = GTK_LIST(l)->selection;
598 if (sl) { /* Something was selected */
599 l_item = GTK_OBJECT(sl->data);
600 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
602 cfmt = (fmt_data *) clp->data;
604 cur_fmt = get_column_format_from_str(cfmt->fmt);
605 gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
607 if (clp != g_list_first(prefs.col_list))
609 if (clp != g_list_last(prefs.col_list))
614 /* Did you know that this function is called when the window is destroyed? */
616 if (!gtk_object_get_data(GTK_OBJECT(data), E_COL_CM_KEY)) {
617 gtk_entry_set_text(GTK_ENTRY(title_te), title);
618 gtk_widget_set_sensitive(chg_bt, sensitivity);
619 gtk_widget_set_sensitive(del_bt, sensitivity);
620 gtk_widget_set_sensitive(up_bt, up_sens);
621 gtk_widget_set_sensitive(dn_bt, dn_sens);
625 /* To do: add input checking to each of these callbacks */
628 column_sel_new_cb(GtkWidget *w, gpointer data) {
631 GtkWidget *nl_item, *nl_lb;
633 title = gtk_entry_get_text(GTK_ENTRY(title_te));
635 if (strlen(title) > 0) {
636 cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
637 cfmt->title = g_strdup(title);
638 cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
639 prefs.col_list = g_list_append(prefs.col_list, cfmt);
640 nl_lb = gtk_label_new(cfmt->title);
641 nl_item = gtk_list_item_new();
642 gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
643 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
644 gtk_widget_show(nl_lb);
645 gtk_container_add(GTK_CONTAINER(column_l), nl_item);
646 gtk_widget_show(nl_item);
647 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
648 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY,
649 g_list_last(prefs.col_list));
650 gtk_list_select_child(GTK_LIST(column_l), nl_item);
655 column_sel_chg_cb(GtkWidget *w, gpointer data) {
662 sl = GTK_LIST(column_l)->selection;
663 title = gtk_entry_get_text(GTK_ENTRY(title_te));
665 if (sl) { /* Something was selected */
666 l_item = GTK_OBJECT(sl->data);
667 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
668 nl_lb = (GtkLabel *) gtk_object_get_data(l_item, E_COL_LBL_KEY);
670 cfmt = (fmt_data *) clp->data;
672 if (strlen(title) > 0 && cfmt) {
675 cfmt->title = g_strdup(title);
676 cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
677 gtk_label_set(nl_lb, cfmt->title);
684 column_sel_del_cb(GtkWidget *w, gpointer data) {
690 sl = GTK_LIST(column_l)->selection;
691 if (sl) { /* Something was selected */
692 l_item = GTK_OBJECT(sl->data);
693 pos = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
694 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
696 cfmt = (fmt_data *) clp->data;
700 prefs.col_list = g_list_remove_link(prefs.col_list, clp);
701 gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
707 column_sel_arrow_cb(GtkWidget *w, gpointer data) {
708 GList *sl, *clp, *il;
716 sl = GTK_LIST(column_l)->selection;
717 if (sl) { /* Something was selected */
718 l_item = GTK_OBJECT(sl->data);
719 pos = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
720 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
722 cfmt = (fmt_data *) clp->data;
723 prefs.col_list = g_list_remove(prefs.col_list, cfmt);
724 g_list_insert(prefs.col_list, cfmt, pos + inc);
725 il = (GList *) g_malloc(sizeof(GList));
729 gtk_widget_ref(GTK_WIDGET(l_item));
730 gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
731 gtk_list_insert_items(GTK_LIST(column_l), il, pos + inc);
732 gtk_widget_unref(GTK_WIDGET(l_item));
733 gtk_list_select_item(GTK_LIST(column_l), pos + inc);
739 column_set_fmt_cb(GtkWidget *w, gpointer data) {
740 cur_fmt = (gint) data;
744 column_prefs_ok(GtkWidget *w) {
746 column_prefs_delete(w);
750 column_prefs_save(GtkWidget *w) {
754 column_prefs_cancel(GtkWidget *w) {
756 column_prefs_delete(w);
760 column_prefs_delete(GtkWidget *w) {
762 /* Let the list cb know we're about to destroy the widget tree, so it */
763 /* doesn't operate on widgets that don't exist. */
764 gtk_object_set_data(GTK_OBJECT(w), E_COL_CM_KEY, (gpointer)TRUE);
765 gtk_widget_destroy(GTK_WIDGET(w));