Removed all references to gtk objects from packet*.[ch] files. They now
[obnox/wireshark/wip.git] / column.c
1 /* column.c
2  * Routines for handling column preferences
3  *
4  * $Id: column.c,v 1.10 1999/03/23 03:14:32 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 #include "packet.h"
46
47 extern e_prefs prefs;
48
49 static GtkWidget *column_l, *chg_bt, *del_bt, *title_te, *fmt_m, *up_bt,
50                  *dn_bt;
51 static gint       cur_fmt;
52
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"
56
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);
66
67 /* Given a format number (as defined in ethereal.h), returns its equivalent
68    string */
69 static gchar *
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",
75                      "%i" };
76   
77   if (fmt < 0 || fmt > NUM_COL_FMTS)
78     return NULL;
79   
80   return(slist[fmt]);
81 }
82
83 /* Given a format number (as defined in ethereal.h), returns its
84   description */
85 static gchar *
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" };
102   
103   if (fmt < 0 || fmt > NUM_COL_FMTS)
104     return NULL;
105   
106   return(dlist[fmt]);
107 }
108
109 /* Marks each array element true if it can be substituted for the given
110    column format */
111 void
112 get_column_format_matches(gboolean *fmt_list, gint format) {
113   int i;
114   
115   for (i = 0; i < NUM_COL_FMTS; i++) {
116     /* Get the obvious: the format itself */
117     if (i == format)
118       fmt_list[i] = TRUE;
119     /* Get any formats lower down on the chain */
120     switch (format) {
121       case COL_CLS_TIME:
122         switch (timestamp_type) {
123           case ABSOLUTE:
124             fmt_list[COL_ABS_TIME] = TRUE;
125             break;
126           case DELTA:
127             fmt_list[COL_DELTA_TIME] = TRUE;
128             break;
129           default:
130             fmt_list[COL_REL_TIME] = TRUE;
131             break;
132         }
133         break;
134       case COL_DEF_SRC:
135         fmt_list[COL_RES_DL_SRC] = TRUE;
136         fmt_list[COL_RES_NET_SRC] = TRUE;
137         break;
138       case COL_RES_SRC:
139         fmt_list[COL_RES_DL_SRC] = TRUE;
140         fmt_list[COL_RES_NET_SRC] = TRUE;
141         break;
142       case COL_UNRES_SRC:
143         fmt_list[COL_UNRES_DL_SRC] = TRUE;
144         fmt_list[COL_UNRES_NET_SRC] = TRUE;
145         break;
146       case COL_DEF_DST:
147         fmt_list[COL_RES_DL_DST] = TRUE;
148         fmt_list[COL_RES_NET_DST] = TRUE;
149         break;
150       case COL_RES_DST:
151         fmt_list[COL_RES_DL_DST] = TRUE;
152         fmt_list[COL_RES_NET_DST] = TRUE;
153         break;
154       case COL_UNRES_DST:
155         fmt_list[COL_UNRES_DL_DST] = TRUE;
156         fmt_list[COL_UNRES_NET_DST] = TRUE;
157         break;
158       case COL_DEF_DL_SRC:
159         fmt_list[COL_RES_DL_SRC] = TRUE;
160         break;
161       case COL_DEF_DL_DST:
162         fmt_list[COL_RES_DL_DST] = TRUE;
163         break;
164       case COL_DEF_NET_SRC:
165         fmt_list[COL_RES_NET_SRC] = TRUE;
166         break;
167       case COL_DEF_NET_DST:
168         fmt_list[COL_RES_NET_DST] = TRUE;
169         break;
170       case COL_DEF_SRC_PORT:
171         fmt_list[COL_RES_SRC_PORT] = TRUE;
172         break;
173       case COL_DEF_DST_PORT:
174         fmt_list[COL_RES_DST_PORT] = TRUE;
175         break;
176       default:
177         break;
178     }
179   }
180 }
181
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. */
187 gint
188 get_column_width(gint format, GdkFont *font) {
189   switch (format) {
190     case COL_NUMBER:
191       return (gdk_string_width(font, "0") * 7);
192       break;
193     case COL_CLS_TIME:
194       if (timestamp_type == COL_ABS_TIME)
195         return (gdk_string_width(font, "00:00:00.000000"));
196       else
197         return (gdk_string_width(font, "0000.000000"));
198       break;
199     case COL_ABS_TIME:
200       return (gdk_string_width(font, "00:00:00.000000"));
201       break;
202     case COL_REL_TIME:
203     case COL_DELTA_TIME:
204       return (gdk_string_width(font, "0000.000000"));
205       break;
206     case COL_DEF_SRC:
207     case COL_RES_SRC:
208     case COL_UNRES_SRC:
209     case COL_DEF_DL_SRC:
210     case COL_RES_DL_SRC:
211     case COL_UNRES_DL_SRC:
212     case COL_DEF_NET_SRC:
213     case COL_RES_NET_SRC:
214     case COL_UNRES_NET_SRC:
215     case COL_DEF_DST:
216     case COL_RES_DST:
217     case COL_UNRES_DST:
218     case COL_DEF_DL_DST:
219     case COL_RES_DL_DST:
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 */
225       break;
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);
233       break;
234     case COL_PROTOCOL:
235       return (gdk_string_width(font, "NBNS (UDP)"));
236       break;
237     default: /* COL_INFO */
238       return (gdk_string_width(font, "Source port: kerberos-master  "
239         "Destination port: kerberos-master"));
240       break;
241   }
242 }
243
244 #define TIME_DEF 0
245 #define TIME_REL 1
246 #define TIME_ABS 2
247 #define TIME_DEL 3
248
249 #define RES_DEF  0
250 #define RES_DO   1
251 #define RES_DONT 2
252
253 #define ADDR_DEF 0
254 #define ADDR_DL  3
255 #define ADDR_NET 6
256
257 gint
258 get_column_format(gint col) {
259   GList    *clp = g_list_nth(prefs.col_list, col);
260   fmt_data *cfmt;
261   
262   cfmt = (fmt_data *) clp->data;
263   
264   return(get_column_format_from_str(cfmt->fmt));
265 }
266
267 static gint
268 get_column_format_from_str(gchar *str) {
269   gchar *cptr = str;
270   gint      res_off = RES_DEF, addr_off = ADDR_DEF, time_off = TIME_DEF;
271
272   /* To do: Make this parse %-formatted strings "for real" */
273   while (*cptr != '\0') {
274     switch (*cptr) {
275       case 't':  /* To do: fix for absolute and delta */
276         return COL_CLS_TIME + time_off;
277         break;
278       case 'm':
279         return COL_NUMBER;
280         break;
281       case 's':
282         return COL_DEF_SRC + res_off + addr_off;
283         break;
284       case 'd':
285         return COL_DEF_DST + res_off + addr_off;
286         break;
287       case 'S':
288         return COL_DEF_SRC_PORT + res_off;
289         break;
290       case 'D':
291         return COL_DEF_DST_PORT + res_off;
292         break;
293       case 'p':
294         return COL_PROTOCOL;
295         break;
296       case 'i':
297         return COL_INFO;
298         break;
299       case 'r':
300         res_off = RES_DO;
301         break;
302       case 'u':
303         res_off = RES_DONT;
304         break;
305       case 'h':
306         addr_off = ADDR_DL;
307         break;
308       case 'n':
309         addr_off = ADDR_NET;
310         break;
311       case 'R':
312         time_off = TIME_REL;
313         break;
314       case 'A':
315         time_off = TIME_ABS;
316         break;
317       case 'T':
318         time_off = TIME_DEL;
319         break;
320     }
321     cptr++;
322   }
323   return COL_NUMBER;
324 }
325
326 gchar *
327 get_column_title(gint col) {
328   GList    *clp = g_list_nth(prefs.col_list, col);
329   fmt_data *cfmt;
330   
331   cfmt = (fmt_data *) clp->data;
332
333   return(cfmt->title);  
334 }
335
336 #define MAX_FMT_PREF_LEN      1024
337 #define MAX_FMT_PREF_LINE_LEN   60
338 gchar *
339 col_format_to_pref_str() {
340   static gchar  pref_str[MAX_FMT_PREF_LEN] = "";
341   GList        *clp = g_list_first(prefs.col_list);
342   fmt_data     *cfmt;
343   int           cur_pos = 0, cur_len = 0, fmt_len;
344   
345   while (clp) {
346     cfmt = (fmt_data *) clp->data;
347     
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) {
351         cur_len--;
352         cur_pos = 0;
353                 pref_str[cur_len] = '\n'; cur_len++;
354         pref_str[cur_len] = '\t'; cur_len++;
355       }
356       sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->title);
357       cur_len += fmt_len;
358       cur_pos += fmt_len;
359     }
360
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) {
364         cur_len--;
365         cur_pos = 0;
366         pref_str[cur_len] = '\n'; cur_len++;
367         pref_str[cur_len] = '\t'; cur_len++;
368       }
369       sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->fmt);
370       cur_len += fmt_len;
371       cur_pos += fmt_len;
372     }
373     
374     clp = clp->next;
375   }
376   
377   if (cur_len > 2)
378     pref_str[cur_len - 2] = '\0';
379
380   return(pref_str);
381 }    
382
383 /* Create and display the column selection widgets. */
384 /* Called when the 'Columns' preference notebook page is selected. */
385 GtkWidget *
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;
389   GList       *clp = NULL;
390   fmt_data    *cfmt;
391   gint         i;
392
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);
398   
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);
403   
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);
408
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);
414   
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);
421   
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);
428   
429   arrow_hb = gtk_hbox_new(TRUE, 3);
430   gtk_container_add(GTK_CONTAINER(list_bb), arrow_hb);
431   gtk_widget_show(arrow_hb);
432   
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);
439   
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);
446   
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);
453
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);
460 #else
461   gtk_container_add(GTK_CONTAINER(column_sc), column_l);
462 #endif
463   gtk_widget_show(column_l);
464
465   clp = g_list_first(prefs.col_list);
466   while (clp) {
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);
477  
478     clp = clp->next;
479   }
480   
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);
486   gtk_widget_show(tb);
487   
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);
491   gtk_widget_show(lb);
492   
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);
496
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);
500   gtk_widget_show(lb);
501
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);
510   }
511   gtk_option_menu_set_menu(GTK_OPTION_MENU(fmt_m), menu);
512   cur_fmt = 0;
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);  
516       
517   return(main_vb);
518 }
519
520 static void
521 column_sel_list_cb(GtkWidget *l, gpointer data) {
522   fmt_data   *cfmt;
523   gchar      *title = "";
524   GList      *sl, *clp;
525   GtkObject  *l_item;
526   gint        sensitivity = FALSE, up_sens = FALSE, dn_sens = FALSE;
527
528   sl = GTK_LIST(l)->selection;
529           
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);
533     if (clp) {
534       cfmt   = (fmt_data *) clp->data;
535       title   = cfmt->title;
536       cur_fmt = get_column_format_from_str(cfmt->fmt);
537       gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
538       sensitivity = TRUE;
539       if (clp != g_list_first(prefs.col_list))
540         up_sens = TRUE;
541       if (clp != g_list_last(prefs.col_list))
542         dn_sens = TRUE;
543     }
544   }
545
546   /* Did you know that this function is called when the window is destroyed? */
547   /* Funny, that. */
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);
554   }
555 }
556
557 /* To do: add input checking to each of these callbacks */
558  
559 static void
560 column_sel_new_cb(GtkWidget *w, gpointer data) {
561   fmt_data   *cfmt;
562   gchar      *title;
563   GtkWidget  *nl_item, *nl_lb;
564   
565   title = gtk_entry_get_text(GTK_ENTRY(title_te));
566   
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);
583   }
584 }
585
586 static void
587 column_sel_chg_cb(GtkWidget *w, gpointer data) {
588   fmt_data   *cfmt;
589   gchar      *title = "";
590   GList      *sl, *clp;
591   GtkObject  *l_item;
592   GtkLabel   *nl_lb;
593
594   sl     = GTK_LIST(column_l)->selection;
595   title  = gtk_entry_get_text(GTK_ENTRY(title_te));
596
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);
601     if (clp && nl_lb) {
602       cfmt = (fmt_data *) clp->data;
603       
604       if (strlen(title) > 0 && cfmt) {
605         g_free(cfmt->title);
606         g_free(cfmt->fmt);
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);
610       }
611     }
612   }
613 }
614
615 static void
616 column_sel_del_cb(GtkWidget *w, gpointer data) {
617   GList      *sl, *clp;
618   fmt_data   *cfmt;
619   GtkObject  *l_item;
620   gint        pos;
621   
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);
627     if (clp) {
628       cfmt = (fmt_data *) clp->data;
629       g_free(cfmt->title);
630       g_free(cfmt->fmt);
631       g_free(cfmt);
632       prefs.col_list = g_list_remove_link(prefs.col_list, clp);
633       gtk_list_clear_items(GTK_LIST(column_l), pos, pos + 1);
634     } 
635   }
636 }
637
638 static void
639 column_sel_arrow_cb(GtkWidget *w, gpointer data) {
640   GList      *sl, *clp, *il;
641   fmt_data   *cfmt;
642   GtkObject  *l_item;
643   gint        pos, inc = 1;
644   
645   if (w == up_bt)
646     inc = -1;
647   
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);
653     if (clp) {
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));
658       il->next = NULL;
659       il->prev = NULL;
660       il->data = l_item;
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);
666     } 
667   }
668 }
669
670 void
671 column_set_fmt_cb(GtkWidget *w, gpointer data) {
672   cur_fmt = (gint) data;
673 }
674
675 void
676 column_prefs_ok(GtkWidget *w) {
677
678   column_prefs_cancel(w);
679 }
680
681 void
682 column_prefs_save(GtkWidget *w) {
683 }
684
685 void
686 column_prefs_cancel(GtkWidget *w) {
687  
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));
692