2 * Routines for handling column preferences
4 * $Id: column.c,v 1.11 1999/06/19 01:14:49 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>
42 #include "timestamp.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 fmt_list[COL_RES_DL_SRC] = TRUE;
123 fmt_list[COL_RES_NET_SRC] = TRUE;
126 fmt_list[COL_RES_DL_SRC] = TRUE;
127 fmt_list[COL_RES_NET_SRC] = TRUE;
130 fmt_list[COL_UNRES_DL_SRC] = TRUE;
131 fmt_list[COL_UNRES_NET_SRC] = TRUE;
134 fmt_list[COL_RES_DL_DST] = TRUE;
135 fmt_list[COL_RES_NET_DST] = TRUE;
138 fmt_list[COL_RES_DL_DST] = TRUE;
139 fmt_list[COL_RES_NET_DST] = TRUE;
142 fmt_list[COL_UNRES_DL_DST] = TRUE;
143 fmt_list[COL_UNRES_NET_DST] = TRUE;
146 fmt_list[COL_RES_DL_SRC] = TRUE;
149 fmt_list[COL_RES_DL_DST] = TRUE;
151 case COL_DEF_NET_SRC:
152 fmt_list[COL_RES_NET_SRC] = TRUE;
154 case COL_DEF_NET_DST:
155 fmt_list[COL_RES_NET_DST] = TRUE;
157 case COL_DEF_SRC_PORT:
158 fmt_list[COL_RES_SRC_PORT] = TRUE;
160 case COL_DEF_DST_PORT:
161 fmt_list[COL_RES_DST_PORT] = TRUE;
169 /* Returns the longest possible width for a particular column type */
170 /* XXX - this is somewhat fragile; we should probably generate */
171 /* the summary lines for all the packets first, and compute the */
172 /* maximum column width as the maximum string width of all the */
173 /* values in that column. */
175 get_column_width(gint format, GdkFont *font) {
178 return (gdk_string_width(font, "0") * 7);
181 if (timestamp_type == COL_ABS_TIME)
182 return (gdk_string_width(font, "00:00:00.000000"));
184 return (gdk_string_width(font, "0000.000000"));
187 return (gdk_string_width(font, "00:00:00.000000"));
191 return (gdk_string_width(font, "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 (gdk_string_width(font, "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:
219 return (gdk_string_width(font, "0") * 6);
222 return (gdk_string_width(font, "NBNS (UDP)"));
224 default: /* COL_INFO */
225 return (gdk_string_width(font, "Source port: kerberos-master "
226 "Destination port: kerberos-master"));
245 get_column_format(gint col) {
246 GList *clp = g_list_nth(prefs.col_list, col);
249 cfmt = (fmt_data *) clp->data;
251 return(get_column_format_from_str(cfmt->fmt));
255 get_column_format_from_str(gchar *str) {
257 gint res_off = RES_DEF, addr_off = ADDR_DEF, time_off = TIME_DEF;
259 /* To do: Make this parse %-formatted strings "for real" */
260 while (*cptr != '\0') {
262 case 't': /* To do: fix for absolute and delta */
263 return COL_CLS_TIME + time_off;
269 return COL_DEF_SRC + res_off + addr_off;
272 return COL_DEF_DST + res_off + addr_off;
275 return COL_DEF_SRC_PORT + res_off;
278 return COL_DEF_DST_PORT + res_off;
314 get_column_title(gint col) {
315 GList *clp = g_list_nth(prefs.col_list, col);
318 cfmt = (fmt_data *) clp->data;
323 #define MAX_FMT_PREF_LEN 1024
324 #define MAX_FMT_PREF_LINE_LEN 60
326 col_format_to_pref_str() {
327 static gchar pref_str[MAX_FMT_PREF_LEN] = "";
328 GList *clp = g_list_first(prefs.col_list);
330 int cur_pos = 0, cur_len = 0, fmt_len;
333 cfmt = (fmt_data *) clp->data;
335 fmt_len = strlen(cfmt->title) + 4;
336 if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
337 if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
340 pref_str[cur_len] = '\n'; cur_len++;
341 pref_str[cur_len] = '\t'; cur_len++;
343 sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->title);
348 fmt_len = strlen(cfmt->fmt) + 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->fmt);
365 pref_str[cur_len - 2] = '\0';
370 /* Create and display the column selection widgets. */
371 /* Called when the 'Columns' preference notebook page is selected. */
373 column_prefs_show() {
374 GtkWidget *main_vb, *top_hb, *list_bb, *new_bt, *column_sc, *nl_item,
375 *nl_lb, *tb, *lb, *menu, *mitem, *arrow_hb;
380 /* Container for each row of widgets */
381 main_vb = gtk_vbox_new(FALSE, 5);
382 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
383 gtk_widget_show(main_vb);
384 gtk_object_set_data(GTK_OBJECT(main_vb), E_COL_CM_KEY, (gpointer)FALSE);
386 /* Top row: Column list and buttons */
387 top_hb = gtk_hbox_new(FALSE, 5);
388 gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
389 gtk_widget_show(top_hb);
391 list_bb = gtk_vbutton_box_new();
392 gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
393 gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
394 gtk_widget_show(list_bb);
396 new_bt = gtk_button_new_with_label ("New");
397 gtk_signal_connect(GTK_OBJECT(new_bt), "clicked",
398 GTK_SIGNAL_FUNC(column_sel_new_cb), NULL);
399 gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
400 gtk_widget_show(new_bt);
402 chg_bt = gtk_button_new_with_label ("Change");
403 gtk_widget_set_sensitive(chg_bt, FALSE);
404 gtk_signal_connect(GTK_OBJECT(chg_bt), "clicked",
405 GTK_SIGNAL_FUNC(column_sel_chg_cb), NULL);
406 gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
407 gtk_widget_show(chg_bt);
409 del_bt = gtk_button_new_with_label ("Delete");
410 gtk_widget_set_sensitive(del_bt, FALSE);
411 gtk_signal_connect(GTK_OBJECT(del_bt), "clicked",
412 GTK_SIGNAL_FUNC(column_sel_del_cb), NULL);
413 gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
414 gtk_widget_show(del_bt);
416 arrow_hb = gtk_hbox_new(TRUE, 3);
417 gtk_container_add(GTK_CONTAINER(list_bb), arrow_hb);
418 gtk_widget_show(arrow_hb);
420 up_bt = gtk_button_new_with_label("Up");
421 gtk_widget_set_sensitive(up_bt, FALSE);
422 gtk_signal_connect(GTK_OBJECT(up_bt), "clicked",
423 GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
424 gtk_box_pack_start(GTK_BOX(arrow_hb), up_bt, TRUE, TRUE, 0);
425 gtk_widget_show(up_bt);
427 dn_bt = gtk_button_new_with_label("Down");
428 gtk_widget_set_sensitive(dn_bt, FALSE);
429 gtk_signal_connect(GTK_OBJECT(dn_bt), "clicked",
430 GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
431 gtk_box_pack_start(GTK_BOX(arrow_hb), dn_bt, TRUE, TRUE, 0);
432 gtk_widget_show(dn_bt);
434 column_sc = gtk_scrolled_window_new(NULL, NULL);
435 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(column_sc),
436 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
437 gtk_widget_set_usize(column_sc, 250, 150);
438 gtk_container_add(GTK_CONTAINER(top_hb), column_sc);
439 gtk_widget_show(column_sc);
441 column_l = gtk_list_new();
442 gtk_list_set_selection_mode(GTK_LIST(column_l), GTK_SELECTION_SINGLE);
443 gtk_signal_connect(GTK_OBJECT(column_l), "selection_changed",
444 GTK_SIGNAL_FUNC(column_sel_list_cb), main_vb);
445 #ifdef GTK_HAVE_FEATURES_1_1_4
446 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(column_sc), column_l);
448 gtk_container_add(GTK_CONTAINER(column_sc), column_l);
450 gtk_widget_show(column_l);
452 clp = g_list_first(prefs.col_list);
454 cfmt = (fmt_data *) clp->data;
455 nl_lb = gtk_label_new(cfmt->title);
456 nl_item = gtk_list_item_new();
457 gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
458 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
459 gtk_widget_show(nl_lb);
460 gtk_container_add(GTK_CONTAINER(column_l), nl_item);
461 gtk_widget_show(nl_item);
462 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
463 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY, clp);
468 /* Colunm name entry and format selection */
469 tb = gtk_table_new(2, 2, FALSE);
470 gtk_container_add(GTK_CONTAINER(main_vb), tb);
471 gtk_table_set_row_spacings(GTK_TABLE(tb), 10);
472 gtk_table_set_col_spacings(GTK_TABLE(tb), 15);
475 lb = gtk_label_new("Column title:");
476 gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
477 gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 0, 1);
480 title_te = gtk_entry_new();
481 gtk_table_attach_defaults(GTK_TABLE(tb), title_te, 1, 2, 0, 1);
482 gtk_widget_show(title_te);
484 lb = gtk_label_new("Column format:");
485 gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
486 gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 1, 2);
489 fmt_m = gtk_option_menu_new();
490 menu = gtk_menu_new();
491 for (i = 0; i < NUM_COL_FMTS; i++) {
492 mitem = gtk_menu_item_new_with_label(col_format_desc(i));
493 gtk_menu_append(GTK_MENU(menu), mitem);
494 gtk_signal_connect_object( GTK_OBJECT(mitem), "activate",
495 GTK_SIGNAL_FUNC(column_set_fmt_cb), (gpointer) i);
496 gtk_widget_show(mitem);
498 gtk_option_menu_set_menu(GTK_OPTION_MENU(fmt_m), menu);
500 gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
501 gtk_table_attach_defaults(GTK_TABLE(tb), fmt_m, 1, 2, 1, 2);
502 gtk_widget_show(fmt_m);
508 column_sel_list_cb(GtkWidget *l, gpointer data) {
513 gint sensitivity = FALSE, up_sens = FALSE, dn_sens = FALSE;
515 sl = GTK_LIST(l)->selection;
517 if (sl) { /* Something was selected */
518 l_item = GTK_OBJECT(sl->data);
519 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
521 cfmt = (fmt_data *) clp->data;
523 cur_fmt = get_column_format_from_str(cfmt->fmt);
524 gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
526 if (clp != g_list_first(prefs.col_list))
528 if (clp != g_list_last(prefs.col_list))
533 /* Did you know that this function is called when the window is destroyed? */
535 if (!gtk_object_get_data(GTK_OBJECT(data), E_COL_CM_KEY)) {
536 gtk_entry_set_text(GTK_ENTRY(title_te), title);
537 gtk_widget_set_sensitive(chg_bt, sensitivity);
538 gtk_widget_set_sensitive(del_bt, sensitivity);
539 gtk_widget_set_sensitive(up_bt, up_sens);
540 gtk_widget_set_sensitive(dn_bt, dn_sens);
544 /* To do: add input checking to each of these callbacks */
547 column_sel_new_cb(GtkWidget *w, gpointer data) {
550 GtkWidget *nl_item, *nl_lb;
552 title = gtk_entry_get_text(GTK_ENTRY(title_te));
554 if (strlen(title) > 0) {
555 cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
556 cfmt->title = g_strdup(title);
557 cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
558 prefs.col_list = g_list_append(prefs.col_list, cfmt);
559 nl_lb = gtk_label_new(cfmt->title);
560 nl_item = gtk_list_item_new();
561 gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
562 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
563 gtk_widget_show(nl_lb);
564 gtk_container_add(GTK_CONTAINER(column_l), nl_item);
565 gtk_widget_show(nl_item);
566 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
567 gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY,
568 g_list_last(prefs.col_list));
569 gtk_list_select_child(GTK_LIST(column_l), nl_item);
574 column_sel_chg_cb(GtkWidget *w, gpointer data) {
581 sl = GTK_LIST(column_l)->selection;
582 title = gtk_entry_get_text(GTK_ENTRY(title_te));
584 if (sl) { /* Something was selected */
585 l_item = GTK_OBJECT(sl->data);
586 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
587 nl_lb = (GtkLabel *) gtk_object_get_data(l_item, E_COL_LBL_KEY);
589 cfmt = (fmt_data *) clp->data;
591 if (strlen(title) > 0 && cfmt) {
594 cfmt->title = g_strdup(title);
595 cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
596 gtk_label_set(nl_lb, cfmt->title);
603 column_sel_del_cb(GtkWidget *w, gpointer data) {
609 sl = GTK_LIST(column_l)->selection;
610 if (sl) { /* Something was selected */
611 l_item = GTK_OBJECT(sl->data);
612 pos = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
613 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
615 cfmt = (fmt_data *) clp->data;
619 prefs.col_list = g_list_remove_link(prefs.col_list, clp);
620 gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
626 column_sel_arrow_cb(GtkWidget *w, gpointer data) {
627 GList *sl, *clp, *il;
635 sl = GTK_LIST(column_l)->selection;
636 if (sl) { /* Something was selected */
637 l_item = GTK_OBJECT(sl->data);
638 pos = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
639 clp = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
641 cfmt = (fmt_data *) clp->data;
642 prefs.col_list = g_list_remove(prefs.col_list, cfmt);
643 g_list_insert(prefs.col_list, cfmt, pos + inc);
644 il = (GList *) g_malloc(sizeof(GList));
648 gtk_widget_ref(GTK_WIDGET(l_item));
649 gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
650 gtk_list_insert_items(GTK_LIST(column_l), il, pos + inc);
651 gtk_widget_unref(GTK_WIDGET(l_item));
652 gtk_list_select_item(GTK_LIST(column_l), pos + inc);
658 column_set_fmt_cb(GtkWidget *w, gpointer data) {
659 cur_fmt = (gint) data;
663 column_prefs_ok(GtkWidget *w) {
665 column_prefs_cancel(w);
669 column_prefs_save(GtkWidget *w) {
673 column_prefs_cancel(GtkWidget *w) {
675 /* Let the list cb know we're about to destroy the widget tree, so it */
676 /* doesn't operate on widgets that don't exist. */
677 gtk_object_set_data(GTK_OBJECT(w), E_COL_CM_KEY, (gpointer)TRUE);
678 gtk_widget_destroy(GTK_WIDGET(w));