1a67e1ec094ef6173da7dd2f385410ec93db38f5
[obnox/wireshark/wip.git] / column.c
1 /* column.c
2  * Routines for handling column preferences
3  *
4  * $Id: column.c,v 1.3 1998/11/18 03:01:26 gerald 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 <ctype.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <sys/stat.h>
40
41 #include "ethereal.h"
42 #include "prefs.h"
43 #include "column.h"
44
45 extern e_prefs prefs;
46
47 static GtkWidget *column_l, *chg_bt, *del_bt, *title_te, *fmt_m, *up_bt,
48                  *dn_bt;
49 static gint       cur_fmt;
50
51 #define E_COL_NAME_KEY "column_name"
52 #define E_COL_LBL_KEY  "column_label"
53 #define E_COL_CM_KEY   "in_col_cancel_mode"
54
55 static gchar *col_format_to_string(gint);
56 static gchar *col_format_desc(gint);
57 static gint   get_column_format_from_str(gchar *str);
58 static void   column_sel_list_cb(GtkWidget *, gpointer);
59 static void   column_sel_new_cb(GtkWidget *, gpointer);
60 static void   column_sel_chg_cb(GtkWidget *, gpointer);
61 static void   column_sel_del_cb(GtkWidget *, gpointer);
62 static void   column_sel_arrow_cb(GtkWidget *, gpointer);
63 static void   column_set_fmt_cb(GtkWidget *, gpointer);
64
65 /* Given a format number (as defined in ethereal.h), returns its equivalent
66    string */
67 static gchar *
68 col_format_to_string(gint fmt) {
69   gchar *slist[] = { "%m", "%t", "%t", "%t", "%s", "%rs", "%us", "%hs",
70                      "%rhs", "%uhs", "%ns", "%rns", "%uns", "%d", "%rd",
71                      "%ud", "%hd", "%rhd", "%uhd", "%nd", "%rnd", "%und",
72                      "%S", "%rS", "%uS", "%D", "%rD", "%uD", "%p", "%i" };
73   
74   if (fmt < 0 || fmt > NUM_COL_FMTS)
75     return NULL;
76   
77   return(slist[fmt]);
78 }
79
80 /* Given a format number (as defined in ethereal.h), returns its
81   description */
82 static gchar *
83 col_format_desc(gint fmt) {
84   gchar *dlist[] = { "Number", "Relative time", "Absolute time",
85                      "Delta time", "Source address", "Src addr (resolved)",
86                      "Src addr (unresolved)", "Hardware src addr",
87                      "Hw src addr (resolved)", "Hw src addr (unresolved)",
88                      "Network src addr", "Net scr addr (resolved)",
89                      "Net src addr (unresolved)", "Destination address",
90                      "Dest addr (resolved)", "Dest addr (unresolved)",
91                      "Hardware dest addr", "Hw dest addr (resolved)",
92                      "Hw dest addr (unresolved)", "Network dest addr",
93                      "Net dest addr (resolved)", "Net dest addr (unresolved)",
94                      "Source port", "Src port (resolved)",
95                      "Src port (unresolved)", "Destination port",
96                      "Dest port (resolved)", "Dest port (unresolved)",
97                      "Protocol", "Information" };
98   
99   if (fmt < 0 || fmt > NUM_COL_FMTS)
100     return NULL;
101   
102   return(dlist[fmt]);
103 }
104
105 /* Marks each array element true if it can be substituted for the given
106    column format */
107 void
108 get_column_format_matches(gboolean *fmt_list, gint format) {
109   int i;
110   
111   for (i = 0; i < NUM_COL_FMTS; i++) {
112     /* Get the obvious: the format itself */
113     if (i == format)
114       fmt_list[i] = TRUE;
115     /* Get any formats lower down on the chain */
116     switch (format) {
117       case COL_DEF_SRC:
118         fmt_list[COL_RES_DL_SRC] = TRUE;
119         fmt_list[COL_RES_NET_SRC] = TRUE;
120         break;
121       case COL_RES_SRC:
122         fmt_list[COL_RES_DL_SRC] = TRUE;
123         fmt_list[COL_RES_NET_SRC] = TRUE;
124         break;
125       case COL_UNRES_SRC:
126         fmt_list[COL_UNRES_DL_SRC] = TRUE;
127         fmt_list[COL_UNRES_NET_SRC] = TRUE;
128         break;
129       case COL_DEF_DST:
130         fmt_list[COL_RES_DL_DST] = TRUE;
131         fmt_list[COL_RES_NET_DST] = TRUE;
132         break;
133       case COL_RES_DST:
134         fmt_list[COL_RES_DL_DST] = TRUE;
135         fmt_list[COL_RES_NET_DST] = TRUE;
136         break;
137       case COL_UNRES_DST:
138         fmt_list[COL_UNRES_DL_DST] = TRUE;
139         fmt_list[COL_UNRES_NET_DST] = TRUE;
140         break;
141       case COL_DEF_DL_SRC:
142         fmt_list[COL_RES_DL_SRC] = TRUE;
143         break;
144       case COL_DEF_DL_DST:
145         fmt_list[COL_RES_DL_DST] = TRUE;
146         break;
147       case COL_DEF_NET_SRC:
148         fmt_list[COL_RES_NET_SRC] = TRUE;
149         break;
150       case COL_DEF_NET_DST:
151         fmt_list[COL_RES_NET_DST] = TRUE;
152         break;
153       case COL_DEF_SRC_PORT:
154         fmt_list[COL_RES_SRC_PORT] = TRUE;
155         break;
156       case COL_DEF_DST_PORT:
157         fmt_list[COL_RES_DST_PORT] = TRUE;
158         break;
159       default:
160         break;
161     }
162   }
163 }
164
165 /* Returns the longest possible width for a particular column type */
166 gint
167 get_column_width(gint format, GdkFont *font) {
168   switch (format) {
169     case COL_NUMBER:
170       return (gdk_string_width(font, "0") * 7);
171       break;
172     case COL_ABS_TIME:
173       return (gdk_string_width(font, "00:00:00.000000"));
174       break;
175     case COL_REL_TIME:
176     case COL_DELTA_TIME:
177       return (gdk_string_width(font, "0000.000000"));
178       break;
179     case COL_DEF_SRC:
180     case COL_RES_SRC:
181     case COL_UNRES_SRC:
182     case COL_DEF_DL_SRC:
183     case COL_RES_DL_SRC:
184     case COL_UNRES_DL_SRC:
185     case COL_DEF_NET_SRC:
186     case COL_RES_NET_SRC:
187     case COL_UNRES_NET_SRC:
188     case COL_DEF_DST:
189     case COL_RES_DST:
190     case COL_UNRES_DST:
191     case COL_DEF_DL_DST:
192     case COL_RES_DL_DST:
193     case COL_UNRES_DL_DST:
194     case COL_DEF_NET_DST:
195     case COL_RES_NET_DST:
196     case COL_UNRES_NET_DST:
197       return (gdk_string_width(font, "00:00:00:00:00:00"));
198       break;
199     case COL_DEF_SRC_PORT:
200     case COL_RES_SRC_PORT:
201     case COL_UNRES_SRC_PORT:
202     case COL_DEF_DST_PORT:
203     case COL_RES_DST_PORT:
204     case COL_UNRES_DST_PORT:
205       return (gdk_string_width(font, "0") * 6);
206       break;
207     case COL_PROTOCOL:
208       return (gdk_string_width(font, "NBNS (UDP)"));
209       break;
210     default: /* COL_INFO */
211       return (gdk_string_width(font, "Source port: kerberos-master  "
212         "Destination port: kerberos-master"));
213       break;
214   }
215 }
216     
217 #define RES_DEF  0
218 #define RES_DO   1
219 #define RES_DONT 2
220
221 #define ADDR_DEF 0
222 #define ADDR_DL  3
223 #define ADDR_NET 6
224
225 gint
226 get_column_format(gint col) {
227   GList    *clp = g_list_nth(prefs.col_list, col);
228   fmt_data *cfmt;
229   
230   cfmt = (fmt_data *) clp->data;
231   
232   return(get_column_format_from_str(cfmt->fmt));
233 }
234
235 static gint
236 get_column_format_from_str(gchar *str) {
237   gchar *cptr = str;
238   gint      res_off = RES_DEF, addr_off = ADDR_DEF;
239
240   /* To do: Make this parse %-formatted strings "for real" */
241   while (*cptr != '\0') {
242     switch (*cptr) {
243       case 't':  /* To do: fix for absolute and delta */
244         return COL_REL_TIME;
245         break;
246       case 'm':
247         return COL_NUMBER;
248         break;
249       case 's':
250         return COL_DEF_SRC + res_off + addr_off;
251         break;
252       case 'd':
253         return COL_DEF_DST + res_off + addr_off;
254         break;
255       case 'S':
256         return COL_DEF_SRC_PORT + res_off;
257         break;
258       case 'D':
259         return COL_DEF_DST_PORT + res_off;
260         break;
261       case 'p':
262         return COL_PROTOCOL;
263         break;
264       case 'i':
265         return COL_INFO;
266         break;
267       case 'r':
268         res_off = RES_DO;
269         break;
270       case 'u':
271         res_off = RES_DONT;
272         break;
273       case 'h':
274         addr_off = ADDR_DL;
275         break;
276       case 'n':
277         addr_off = ADDR_NET;
278         break;
279     }
280     cptr++;
281   }
282   return COL_NUMBER;
283 }
284
285 gchar *
286 get_column_title(gint col) {
287   GList    *clp = g_list_nth(prefs.col_list, col);
288   fmt_data *cfmt;
289   
290   cfmt = (fmt_data *) clp->data;
291
292   return(cfmt->title);  
293 }
294
295 #define MAX_FMT_PREF_LEN      1024
296 #define MAX_FMT_PREF_LINE_LEN   60
297 gchar *
298 col_format_to_pref_str() {
299   static gchar  pref_str[MAX_FMT_PREF_LEN] = "";
300   GList        *clp = g_list_first(prefs.col_list);
301   fmt_data     *cfmt;
302   int           cur_pos = 0, cur_len = 0, fmt_len;
303   
304   while (clp) {
305     cfmt = (fmt_data *) clp->data;
306     
307     fmt_len = strlen(cfmt->title) + 4;
308     if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
309       if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
310         cur_len--;
311         cur_pos = 0;
312         pref_str[cur_len] = '\n'; cur_len++;
313         pref_str[cur_len] = '\t'; cur_len++;
314       }
315       sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->title);
316       cur_len += fmt_len;
317       cur_pos += fmt_len;
318     }
319
320     fmt_len = strlen(cfmt->fmt) + 4;
321     if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
322       if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
323         cur_len--;
324         cur_pos = 0;
325         pref_str[cur_len] = '\n'; cur_len++;
326         pref_str[cur_len] = '\t'; cur_len++;
327       }
328       sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->fmt);
329       cur_len += fmt_len;
330       cur_pos += fmt_len;
331     }
332     
333     clp = clp->next;
334   }
335   
336   if (cur_len > 2)
337     pref_str[cur_len - 2] = '\0';
338
339   return(pref_str);
340 }    
341
342 /* Create and display the column selection widgets. */
343 /* Called when the 'Columns' preference notebook page is selected. */
344 GtkWidget *
345 column_prefs_show() {
346   GtkWidget   *main_vb, *top_hb, *list_bb, *new_bt, *column_sc, *nl_item,
347               *nl_lb, *tb, *lb, *menu, *mitem, *arrow_hb;
348   GList       *clp = NULL;
349   fmt_data    *cfmt;
350   gint         i;
351
352   /* Container for each row of widgets */
353   main_vb = gtk_vbox_new(FALSE, 5);
354   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
355   gtk_widget_show(main_vb);
356   gtk_object_set_data(GTK_OBJECT(main_vb), E_COL_CM_KEY, (gpointer)FALSE);
357   
358   /* Top row: Column list and buttons */
359   top_hb = gtk_hbox_new(FALSE, 5);
360   gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
361   gtk_widget_show(top_hb);
362   
363   list_bb = gtk_vbutton_box_new();
364   gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
365   gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
366   gtk_widget_show(list_bb);
367
368   new_bt = gtk_button_new_with_label ("New");
369   gtk_signal_connect(GTK_OBJECT(new_bt), "clicked",
370     GTK_SIGNAL_FUNC(column_sel_new_cb), NULL);
371   gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
372   gtk_widget_show(new_bt);
373   
374   chg_bt = gtk_button_new_with_label ("Change");
375   gtk_widget_set_sensitive(chg_bt, FALSE);
376   gtk_signal_connect(GTK_OBJECT(chg_bt), "clicked",
377     GTK_SIGNAL_FUNC(column_sel_chg_cb), NULL);
378   gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
379   gtk_widget_show(chg_bt);
380   
381   del_bt = gtk_button_new_with_label ("Delete");
382   gtk_widget_set_sensitive(del_bt, FALSE);
383   gtk_signal_connect(GTK_OBJECT(del_bt), "clicked",
384     GTK_SIGNAL_FUNC(column_sel_del_cb), NULL);
385   gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
386   gtk_widget_show(del_bt);
387   
388   arrow_hb = gtk_hbox_new(TRUE, 3);
389   gtk_container_add(GTK_CONTAINER(list_bb), arrow_hb);
390   gtk_widget_show(arrow_hb);
391   
392   up_bt = gtk_button_new_with_label("Up");
393   gtk_widget_set_sensitive(up_bt, FALSE);
394   gtk_signal_connect(GTK_OBJECT(up_bt), "clicked",
395     GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
396   gtk_box_pack_start(GTK_BOX(arrow_hb), up_bt, TRUE, TRUE, 0);
397   gtk_widget_show(up_bt);
398   
399   dn_bt = gtk_button_new_with_label("Down");
400   gtk_widget_set_sensitive(dn_bt, FALSE);
401   gtk_signal_connect(GTK_OBJECT(dn_bt), "clicked",
402     GTK_SIGNAL_FUNC(column_sel_arrow_cb), NULL);
403   gtk_box_pack_start(GTK_BOX(arrow_hb), dn_bt, TRUE, TRUE, 0);
404   gtk_widget_show(dn_bt);
405   
406   column_sc = gtk_scrolled_window_new(NULL, NULL);
407   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(column_sc),
408     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
409   gtk_widget_set_usize(column_sc, 250, 150);
410   gtk_container_add(GTK_CONTAINER(top_hb), column_sc);
411   gtk_widget_show(column_sc);
412
413   column_l = gtk_list_new();
414   gtk_list_set_selection_mode(GTK_LIST(column_l), GTK_SELECTION_SINGLE);
415   gtk_signal_connect(GTK_OBJECT(column_l), "selection_changed",
416     GTK_SIGNAL_FUNC(column_sel_list_cb), main_vb);
417   gtk_container_add(GTK_CONTAINER(column_sc), column_l);
418   gtk_widget_show(column_l);
419
420   clp = g_list_first(prefs.col_list);
421   while (clp) {
422     cfmt    = (fmt_data *) clp->data;
423     nl_lb   = gtk_label_new(cfmt->title);
424     nl_item = gtk_list_item_new();
425     gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
426     gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
427     gtk_widget_show(nl_lb);
428     gtk_container_add(GTK_CONTAINER(column_l), nl_item);
429     gtk_widget_show(nl_item);
430     gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
431     gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY, clp);
432  
433     clp = clp->next;
434   }
435   
436   /* Colunm name entry and format selection */
437   tb = gtk_table_new(2, 2, FALSE);
438   gtk_container_add(GTK_CONTAINER(main_vb), tb);
439   gtk_table_set_row_spacings(GTK_TABLE(tb), 10);
440   gtk_table_set_col_spacings(GTK_TABLE(tb), 15);
441   gtk_widget_show(tb);
442   
443   lb = gtk_label_new("Column title:");
444   gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
445   gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 0, 1);
446   gtk_widget_show(lb);
447   
448   title_te = gtk_entry_new();
449   gtk_table_attach_defaults(GTK_TABLE(tb), title_te, 1, 2, 0, 1);
450   gtk_widget_show(title_te);
451
452   lb = gtk_label_new("Column format:");
453   gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
454   gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 1, 2);
455   gtk_widget_show(lb);
456
457   fmt_m = gtk_option_menu_new();
458   menu  = gtk_menu_new();
459   for (i = 0; i < NUM_COL_FMTS; i++) {
460     mitem = gtk_menu_item_new_with_label(col_format_desc(i));
461     gtk_menu_append(GTK_MENU(menu), mitem);
462     gtk_signal_connect_object( GTK_OBJECT(mitem), "activate",
463       GTK_SIGNAL_FUNC(column_set_fmt_cb), (gpointer) i);
464     gtk_widget_show(mitem);
465   }
466   gtk_option_menu_set_menu(GTK_OPTION_MENU(fmt_m), menu);
467   cur_fmt = 0;
468   gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
469   gtk_table_attach_defaults(GTK_TABLE(tb), fmt_m, 1, 2, 1, 2);
470   gtk_widget_show(fmt_m);  
471       
472   return(main_vb);
473 }
474
475 static void
476 column_sel_list_cb(GtkWidget *l, gpointer data) {
477   fmt_data   *cfmt;
478   gchar      *title = "";
479   GList      *sl, *clp;
480   GtkObject  *l_item;
481   gint        sensitivity = FALSE, up_sens = FALSE, dn_sens = FALSE;
482
483   sl = GTK_LIST(l)->selection;
484           
485   if (sl) {  /* Something was selected */
486     l_item = GTK_OBJECT(sl->data);
487     clp    = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
488     if (clp) {
489       cfmt   = (fmt_data *) clp->data;
490       title   = cfmt->title;
491       cur_fmt = get_column_format_from_str(cfmt->fmt);
492       gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
493       sensitivity = TRUE;
494       if (clp != g_list_first(prefs.col_list))
495         up_sens = TRUE;
496       if (clp != g_list_last(prefs.col_list))
497         dn_sens = TRUE;
498     }
499   }
500
501   /* Did you know that this function is called when the window is destroyed? */
502   /* Funny, that. */
503   if (!gtk_object_get_data(GTK_OBJECT(data), E_COL_CM_KEY)) {
504     gtk_entry_set_text(GTK_ENTRY(title_te), title);
505     gtk_widget_set_sensitive(chg_bt, sensitivity);
506     gtk_widget_set_sensitive(del_bt, sensitivity);
507     gtk_widget_set_sensitive(up_bt, up_sens);
508     gtk_widget_set_sensitive(dn_bt, dn_sens);
509   }
510 }
511
512 /* To do: add input checking to each of these callbacks */
513  
514 static void
515 column_sel_new_cb(GtkWidget *w, gpointer data) {
516   fmt_data   *cfmt;
517   gchar      *title;
518   GtkWidget  *nl_item, *nl_lb;
519   
520   title = gtk_entry_get_text(GTK_ENTRY(title_te));
521   
522   if (strlen(title) > 0) {
523     cfmt           = (fmt_data *) g_malloc(sizeof(fmt_data));
524     cfmt->title    = g_strdup(title);
525     cfmt->fmt      = g_strdup(col_format_to_string(cur_fmt));
526     prefs.col_list = g_list_append(prefs.col_list, cfmt);
527     nl_lb          = gtk_label_new(cfmt->title);
528     nl_item        = gtk_list_item_new();
529     gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
530     gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
531     gtk_widget_show(nl_lb);
532     gtk_container_add(GTK_CONTAINER(column_l), nl_item);
533     gtk_widget_show(nl_item);
534     gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_LBL_KEY, nl_lb);
535     gtk_object_set_data(GTK_OBJECT(nl_item), E_COL_NAME_KEY,
536       g_list_last(prefs.col_list));
537     gtk_list_select_child(GTK_LIST(column_l), nl_item);
538   }
539 }
540
541 static void
542 column_sel_chg_cb(GtkWidget *w, gpointer data) {
543   fmt_data   *cfmt;
544   gchar      *title = "";
545   GList      *sl, *clp;
546   GtkObject  *l_item;
547   GtkLabel   *nl_lb;
548
549   sl     = GTK_LIST(column_l)->selection;
550   title  = gtk_entry_get_text(GTK_ENTRY(title_te));
551
552   if (sl) {  /* Something was selected */
553     l_item = GTK_OBJECT(sl->data);
554     clp    = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
555     nl_lb  = (GtkLabel *) gtk_object_get_data(l_item, E_COL_LBL_KEY);
556     if (clp && nl_lb) {
557       cfmt = (fmt_data *) clp->data;
558       
559       if (strlen(title) > 0 && cfmt) {
560         g_free(cfmt->title);
561         g_free(cfmt->fmt);
562         cfmt->title = g_strdup(title);
563         cfmt->fmt   = g_strdup(col_format_to_string(cur_fmt));
564         gtk_label_set(nl_lb, cfmt->title);
565       }
566     }
567   }
568 }
569
570 static void
571 column_sel_del_cb(GtkWidget *w, gpointer data) {
572   GList      *sl, *clp;
573   fmt_data   *cfmt;
574   GtkObject  *l_item;
575   gint        pos;
576   
577   sl = GTK_LIST(column_l)->selection;
578   if (sl) {  /* Something was selected */
579     l_item = GTK_OBJECT(sl->data);
580     pos    = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
581     clp    = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
582     if (clp) {
583       cfmt = (fmt_data *) clp->data;
584       g_free(cfmt->title);
585       g_free(cfmt->fmt);
586       g_free(cfmt);
587       prefs.col_list = g_list_remove_link(prefs.col_list, clp);
588       gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
589     } 
590   }
591 }
592
593 static void
594 column_sel_arrow_cb(GtkWidget *w, gpointer data) {
595   GList      *sl, *clp, *il;
596   fmt_data   *cfmt;
597   GtkObject  *l_item;
598   gint        pos, inc = 1;
599   
600   if (w == up_bt)
601     inc = -1;
602   
603   sl = GTK_LIST(column_l)->selection;
604   if (sl) {  /* Something was selected */
605     l_item  = GTK_OBJECT(sl->data);
606     pos     = gtk_list_child_position(GTK_LIST(column_l), GTK_WIDGET(l_item));
607     clp     = (GList *) gtk_object_get_data(l_item, E_COL_NAME_KEY);
608     if (clp) {
609       cfmt = (fmt_data *) clp->data;
610       prefs.col_list = g_list_remove(prefs.col_list, cfmt);
611       g_list_insert(prefs.col_list, cfmt, pos + inc);
612       il = (GList *) g_malloc(sizeof(GList));
613       il->next = NULL;
614       il->prev = NULL;
615       il->data = l_item;
616       gtk_widget_ref(GTK_WIDGET(l_item));
617       gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
618       gtk_list_insert_items(GTK_LIST(column_l), il, pos + inc);
619       gtk_widget_unref(GTK_WIDGET(l_item));
620       gtk_list_select_item(GTK_LIST(column_l), pos + inc);
621     } 
622   }
623 }
624
625 void
626 column_set_fmt_cb(GtkWidget *w, gpointer data) {
627   cur_fmt = (gint) data;
628 }
629
630 void
631 column_prefs_ok(GtkWidget *w) {
632
633   column_prefs_cancel(w);
634 }
635
636 void
637 column_prefs_save(GtkWidget *w) {
638 }
639
640 void
641 column_prefs_cancel(GtkWidget *w) {
642  
643   /* Let the list cb know we're about to destroy the widget tree, so it */
644   /* doesn't operate on widgets that don't exist. */  
645   gtk_object_set_data(GTK_OBJECT(w), E_COL_CM_KEY, (gpointer)TRUE);
646   gtk_widget_destroy(GTK_WIDGET(w));
647