819c3e907cc34b625c157eaea44e44f5d6079edc
[obnox/wireshark/wip.git] / column.c
1 /* column.c
2  * Routines for handling column preferences
3  *
4  * $Id: column.c,v 1.6 1998/12/22 07:07:09 gram Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
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.
15  * 
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.
20  * 
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33
34 #include <gtk/gtk.h>
35
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41
42 #include "ethereal.h"
43 #include "prefs.h"
44 #include "column.h"
45
46 extern e_prefs prefs;
47
48 static GtkWidget *column_l, *chg_bt, *del_bt, *title_te, *fmt_m, *up_bt,
49                  *dn_bt;
50 static gint       cur_fmt;
51
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"
55
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);
65
66 /* Given a format number (as defined in ethereal.h), returns its equivalent
67    string */
68 static gchar *
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" };
74   
75   if (fmt < 0 || fmt > NUM_COL_FMTS)
76     return NULL;
77   
78   return(slist[fmt]);
79 }
80
81 /* Given a format number (as defined in ethereal.h), returns its
82   description */
83 static gchar *
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" };
99   
100   if (fmt < 0 || fmt > NUM_COL_FMTS)
101     return NULL;
102   
103   return(dlist[fmt]);
104 }
105
106 /* Marks each array element true if it can be substituted for the given
107    column format */
108 void
109 get_column_format_matches(gboolean *fmt_list, gint format) {
110   int i;
111   
112   for (i = 0; i < NUM_COL_FMTS; i++) {
113     /* Get the obvious: the format itself */
114     if (i == format)
115       fmt_list[i] = TRUE;
116     /* Get any formats lower down on the chain */
117     switch (format) {
118       case COL_DEF_SRC:
119         fmt_list[COL_RES_DL_SRC] = TRUE;
120         fmt_list[COL_RES_NET_SRC] = TRUE;
121         break;
122       case COL_RES_SRC:
123         fmt_list[COL_RES_DL_SRC] = TRUE;
124         fmt_list[COL_RES_NET_SRC] = TRUE;
125         break;
126       case COL_UNRES_SRC:
127         fmt_list[COL_UNRES_DL_SRC] = TRUE;
128         fmt_list[COL_UNRES_NET_SRC] = TRUE;
129         break;
130       case COL_DEF_DST:
131         fmt_list[COL_RES_DL_DST] = TRUE;
132         fmt_list[COL_RES_NET_DST] = TRUE;
133         break;
134       case COL_RES_DST:
135         fmt_list[COL_RES_DL_DST] = TRUE;
136         fmt_list[COL_RES_NET_DST] = TRUE;
137         break;
138       case COL_UNRES_DST:
139         fmt_list[COL_UNRES_DL_DST] = TRUE;
140         fmt_list[COL_UNRES_NET_DST] = TRUE;
141         break;
142       case COL_DEF_DL_SRC:
143         fmt_list[COL_RES_DL_SRC] = TRUE;
144         break;
145       case COL_DEF_DL_DST:
146         fmt_list[COL_RES_DL_DST] = TRUE;
147         break;
148       case COL_DEF_NET_SRC:
149         fmt_list[COL_RES_NET_SRC] = TRUE;
150         break;
151       case COL_DEF_NET_DST:
152         fmt_list[COL_RES_NET_DST] = TRUE;
153         break;
154       case COL_DEF_SRC_PORT:
155         fmt_list[COL_RES_SRC_PORT] = TRUE;
156         break;
157       case COL_DEF_DST_PORT:
158         fmt_list[COL_RES_DST_PORT] = TRUE;
159         break;
160       default:
161         break;
162     }
163   }
164 }
165
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. */
171 gint
172 get_column_width(gint format, GdkFont *font) {
173   switch (format) {
174     case COL_NUMBER:
175       return (gdk_string_width(font, "0") * 7);
176       break;
177     case COL_ABS_TIME:
178       return (gdk_string_width(font, "00:00:00.000000"));
179       break;
180     case COL_REL_TIME:
181     case COL_DELTA_TIME:
182       return (gdk_string_width(font, "0000.000000"));
183       break;
184     case COL_DEF_SRC:
185     case COL_RES_SRC:
186     case COL_UNRES_SRC:
187     case COL_DEF_DL_SRC:
188     case COL_RES_DL_SRC:
189     case COL_UNRES_DL_SRC:
190     case COL_DEF_NET_SRC:
191     case COL_RES_NET_SRC:
192     case COL_UNRES_NET_SRC:
193     case COL_DEF_DST:
194     case COL_RES_DST:
195     case COL_UNRES_DST:
196     case COL_DEF_DL_DST:
197     case COL_RES_DL_DST:
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"));
203       break;
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);
211       break;
212     case COL_PROTOCOL:
213       return (gdk_string_width(font, "NBNS (UDP)"));
214       break;
215     default: /* COL_INFO */
216       return (gdk_string_width(font, "Source port: kerberos-master  "
217         "Destination port: kerberos-master"));
218       break;
219   }
220 }
221     
222 #define RES_DEF  0
223 #define RES_DO   1
224 #define RES_DONT 2
225
226 #define ADDR_DEF 0
227 #define ADDR_DL  3
228 #define ADDR_NET 6
229
230 gint
231 get_column_format(gint col) {
232   GList    *clp = g_list_nth(prefs.col_list, col);
233   fmt_data *cfmt;
234   
235   cfmt = (fmt_data *) clp->data;
236   
237   return(get_column_format_from_str(cfmt->fmt));
238 }
239
240 static gint
241 get_column_format_from_str(gchar *str) {
242   gchar *cptr = str;
243   gint      res_off = RES_DEF, addr_off = ADDR_DEF;
244
245   /* To do: Make this parse %-formatted strings "for real" */
246   while (*cptr != '\0') {
247     switch (*cptr) {
248       case 't':  /* To do: fix for absolute and delta */
249         return COL_REL_TIME;
250         break;
251       case 'm':
252         return COL_NUMBER;
253         break;
254       case 's':
255         return COL_DEF_SRC + res_off + addr_off;
256         break;
257       case 'd':
258         return COL_DEF_DST + res_off + addr_off;
259         break;
260       case 'S':
261         return COL_DEF_SRC_PORT + res_off;
262         break;
263       case 'D':
264         return COL_DEF_DST_PORT + res_off;
265         break;
266       case 'p':
267         return COL_PROTOCOL;
268         break;
269       case 'i':
270         return COL_INFO;
271         break;
272       case 'r':
273         res_off = RES_DO;
274         break;
275       case 'u':
276         res_off = RES_DONT;
277         break;
278       case 'h':
279         addr_off = ADDR_DL;
280         break;
281       case 'n':
282         addr_off = ADDR_NET;
283         break;
284     }
285     cptr++;
286   }
287   return COL_NUMBER;
288 }
289
290 gchar *
291 get_column_title(gint col) {
292   GList    *clp = g_list_nth(prefs.col_list, col);
293   fmt_data *cfmt;
294   
295   cfmt = (fmt_data *) clp->data;
296
297   return(cfmt->title);  
298 }
299
300 #define MAX_FMT_PREF_LEN      1024
301 #define MAX_FMT_PREF_LINE_LEN   60
302 gchar *
303 col_format_to_pref_str() {
304   static gchar  pref_str[MAX_FMT_PREF_LEN] = "";
305   GList        *clp = g_list_first(prefs.col_list);
306   fmt_data     *cfmt;
307   int           cur_pos = 0, cur_len = 0, fmt_len;
308   
309   while (clp) {
310     cfmt = (fmt_data *) clp->data;
311     
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) {
315         cur_len--;
316         cur_pos = 0;
317         pref_str[cur_len] = '\n'; cur_len++;
318         pref_str[cur_len] = '\t'; cur_len++;
319       }
320       sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->title);
321       cur_len += fmt_len;
322       cur_pos += fmt_len;
323     }
324
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) {
328         cur_len--;
329         cur_pos = 0;
330         pref_str[cur_len] = '\n'; cur_len++;
331         pref_str[cur_len] = '\t'; cur_len++;
332       }
333       sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->fmt);
334       cur_len += fmt_len;
335       cur_pos += fmt_len;
336     }
337     
338     clp = clp->next;
339   }
340   
341   if (cur_len > 2)
342     pref_str[cur_len - 2] = '\0';
343
344   return(pref_str);
345 }    
346
347 /* Create and display the column selection widgets. */
348 /* Called when the 'Columns' preference notebook page is selected. */
349 GtkWidget *
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;
353   GList       *clp = NULL;
354   fmt_data    *cfmt;
355   gint         i;
356
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);
362   
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);
367   
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);
372
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);
378   
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);
385   
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);
392   
393   arrow_hb = gtk_hbox_new(TRUE, 3);
394   gtk_container_add(GTK_CONTAINER(list_bb), arrow_hb);
395   gtk_widget_show(arrow_hb);
396   
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);
403   
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);
410   
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);
417
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);
424 #else
425   gtk_container_add(GTK_CONTAINER(column_sc), column_l);
426 #endif
427   gtk_widget_show(column_l);
428
429   clp = g_list_first(prefs.col_list);
430   while (clp) {
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);
441  
442     clp = clp->next;
443   }
444   
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);
450   gtk_widget_show(tb);
451   
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);
455   gtk_widget_show(lb);
456   
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);
460
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);
464   gtk_widget_show(lb);
465
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);
474   }
475   gtk_option_menu_set_menu(GTK_OPTION_MENU(fmt_m), menu);
476   cur_fmt = 0;
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);  
480       
481   return(main_vb);
482 }
483
484 static void
485 column_sel_list_cb(GtkWidget *l, gpointer data) {
486   fmt_data   *cfmt;
487   gchar      *title = "";
488   GList      *sl, *clp;
489   GtkObject  *l_item;
490   gint        sensitivity = FALSE, up_sens = FALSE, dn_sens = FALSE;
491
492   sl = GTK_LIST(l)->selection;
493           
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);
497     if (clp) {
498       cfmt   = (fmt_data *) clp->data;
499       title   = cfmt->title;
500       cur_fmt = get_column_format_from_str(cfmt->fmt);
501       gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
502       sensitivity = TRUE;
503       if (clp != g_list_first(prefs.col_list))
504         up_sens = TRUE;
505       if (clp != g_list_last(prefs.col_list))
506         dn_sens = TRUE;
507     }
508   }
509
510   /* Did you know that this function is called when the window is destroyed? */
511   /* Funny, that. */
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);
518   }
519 }
520
521 /* To do: add input checking to each of these callbacks */
522  
523 static void
524 column_sel_new_cb(GtkWidget *w, gpointer data) {
525   fmt_data   *cfmt;
526   gchar      *title;
527   GtkWidget  *nl_item, *nl_lb;
528   
529   title = gtk_entry_get_text(GTK_ENTRY(title_te));
530   
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);
547   }
548 }
549
550 static void
551 column_sel_chg_cb(GtkWidget *w, gpointer data) {
552   fmt_data   *cfmt;
553   gchar      *title = "";
554   GList      *sl, *clp;
555   GtkObject  *l_item;
556   GtkLabel   *nl_lb;
557
558   sl     = GTK_LIST(column_l)->selection;
559   title  = gtk_entry_get_text(GTK_ENTRY(title_te));
560
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);
565     if (clp && nl_lb) {
566       cfmt = (fmt_data *) clp->data;
567       
568       if (strlen(title) > 0 && cfmt) {
569         g_free(cfmt->title);
570         g_free(cfmt->fmt);
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);
574       }
575     }
576   }
577 }
578
579 static void
580 column_sel_del_cb(GtkWidget *w, gpointer data) {
581   GList      *sl, *clp;
582   fmt_data   *cfmt;
583   GtkObject  *l_item;
584   gint        pos;
585   
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);
591     if (clp) {
592       cfmt = (fmt_data *) clp->data;
593       g_free(cfmt->title);
594       g_free(cfmt->fmt);
595       g_free(cfmt);
596       prefs.col_list = g_list_remove_link(prefs.col_list, clp);
597       gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
598     } 
599   }
600 }
601
602 static void
603 column_sel_arrow_cb(GtkWidget *w, gpointer data) {
604   GList      *sl, *clp, *il;
605   fmt_data   *cfmt;
606   GtkObject  *l_item;
607   gint        pos, inc = 1;
608   
609   if (w == up_bt)
610     inc = -1;
611   
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);
617     if (clp) {
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));
622       il->next = NULL;
623       il->prev = NULL;
624       il->data = l_item;
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);
630     } 
631   }
632 }
633
634 void
635 column_set_fmt_cb(GtkWidget *w, gpointer data) {
636   cur_fmt = (gint) data;
637 }
638
639 void
640 column_prefs_ok(GtkWidget *w) {
641
642   column_prefs_cancel(w);
643 }
644
645 void
646 column_prefs_save(GtkWidget *w) {
647 }
648
649 void
650 column_prefs_cancel(GtkWidget *w) {
651  
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));
656