ab41c902f0bb78809564b050bcac8fc986d099de
[obnox/wireshark/wip.git] / gtk / main_packet_list.c
1 /* main_packet_list.c
2  * packet list related functions   2002 Olivier Abad
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <gtk/gtk.h>
30 #include <string.h>
31
32 #include <epan/epan.h>
33 #include <epan/column.h>
34 #include <epan/column_info.h>
35 #include <epan/prefs.h>
36 #include <epan/timestamp.h>
37
38 #include "../globals.h"
39 #include "../color.h"
40 #include "../color_filters.h"
41 #include "../ui_util.h"
42 #include "../progress_dlg.h"
43
44 #include "gtk/gtkglobals.h"
45 #include "gtk/gui_utils.h"
46 #include "gtk/color_utils.h"
47 #include "gtk/capture_file_dlg.h"
48 #include "gtk/keys.h"
49 #include "gtk/font_utils.h"
50 #include "gtk/packet_history.h"
51 #include "gtk/recent.h"
52 #include "gtk/main.h"
53 #include "gtk/menus.h"
54 #include "gtk/main_packet_list.h"
55 #include "gtk/main_statusbar.h"
56 #include "gtk/packet_win.h"
57
58 #include "image/clist_ascend.xpm"
59 #include "image/clist_descend.xpm"
60
61 #define N_PROGBAR_UPDATES 100
62
63 typedef struct column_arrows {
64   GtkWidget *table;
65   GtkWidget *ascend_pm;
66   GtkWidget *descend_pm;
67 } column_arrows;
68
69 GtkWidget *packet_list;
70 static gboolean last_at_end = FALSE;
71
72 /* GtkClist compare routine, overrides default to allow numeric comparison */
73
74 #define COMPARE_FRAME_NUM()     ((fdata1->num < fdata2->num) ? -1 : \
75                                  (fdata1->num > fdata2->num) ? 1 : \
76                                  0)
77
78 #define COMPARE_NUM(f)  ((fdata1->f < fdata2->f) ? -1 : \
79                          (fdata1->f > fdata2->f) ? 1 : \
80                          COMPARE_FRAME_NUM())
81
82 /* Compare time stamps.
83    A packet whose time is a reference time is considered to have
84    a lower time stamp than any frame with a non-reference time;
85    if both packets' times are reference times, we compare the
86    times of the packets. */
87 #define COMPARE_TS(ts) \
88                 ((fdata1->flags.ref_time && !fdata2->flags.ref_time) ? -1 : \
89                  (!fdata1->flags.ref_time && fdata2->flags.ref_time) ? 1 : \
90                  (fdata1->ts.secs < fdata2->ts.secs) ? -1 : \
91                  (fdata1->ts.secs > fdata2->ts.secs) ? 1 : \
92                  (fdata1->ts.nsecs < fdata2->ts.nsecs) ? -1 :\
93                  (fdata1->ts.nsecs > fdata2->ts.nsecs) ? 1 : \
94                  COMPARE_FRAME_NUM())
95 static gint
96 packet_list_compare(GtkCList *clist, gconstpointer  ptr1, gconstpointer  ptr2)
97 {
98   /* Get row data structures */
99   const GtkCListRow *row1 = (const GtkCListRow *)ptr1;
100   const GtkCListRow *row2 = (const GtkCListRow *)ptr2;
101
102   /* Get the frame data structures for the rows */
103   const frame_data *fdata1 = row1->data;
104   const frame_data *fdata2 = row2->data;
105
106   /* Get row text strings */
107   const char *text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
108   const char *text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
109
110   /* Attempt to convert to numbers */
111   double  num1;
112   double  num2;
113
114   /* For checking custom column type */
115   header_field_info *hfi;
116   gboolean custom_numeric = FALSE;
117
118   int ret;
119
120   gint  col_fmt = cfile.cinfo.col_fmt[clist->sort_column];
121
122   switch (col_fmt) {
123
124   case COL_NUMBER:
125     return COMPARE_FRAME_NUM();
126
127   case COL_CLS_TIME:
128     switch (timestamp_get_type()) {
129
130     case TS_ABSOLUTE:
131     case TS_ABSOLUTE_WITH_DATE:
132     case TS_EPOCH:
133       return COMPARE_TS(abs_ts);
134
135     case TS_RELATIVE:
136       return COMPARE_TS(rel_ts);
137
138     case TS_DELTA:
139       return COMPARE_TS(del_cap_ts);
140
141     case TS_DELTA_DIS:
142       return COMPARE_TS(del_dis_ts);
143
144     case TS_NOT_SET:
145       return 0;
146     }
147     return 0;
148
149   case COL_ABS_TIME:
150   case COL_ABS_DATE_TIME:
151     return COMPARE_TS(abs_ts);
152
153   case COL_REL_TIME:
154     return COMPARE_TS(rel_ts);
155
156   case COL_DELTA_TIME:
157     return COMPARE_TS(del_cap_ts);
158
159   case COL_DELTA_TIME_DIS:
160     return COMPARE_TS(del_dis_ts);
161
162   case COL_PACKET_LENGTH:
163     return COMPARE_NUM(pkt_len);
164
165   case COL_CUMULATIVE_BYTES:
166     return COMPARE_NUM(cum_bytes);
167
168   case COL_CUSTOM:
169     hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[clist->sort_column]);
170     if (hfi == NULL) {
171         return COMPARE_FRAME_NUM();
172     } else if ((hfi->strings == NULL) &&
173                (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
174                  ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
175                   (hfi->display == BASE_OCT))) ||
176                 (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
177                 (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
178                 (hfi->type == FT_RELATIVE_TIME))) {
179       
180       /* Compare numeric column */
181       custom_numeric = TRUE;
182     }
183     /* FALLTHRU */
184   default:
185     num1 = atof(text1);
186     num2 = atof(text2);
187     if ((col_fmt == COL_UNRES_SRC_PORT) || (col_fmt == COL_UNRES_DST_PORT) ||
188         (custom_numeric) || 
189         ((num1 != 0) && (num2 != 0) && ((col_fmt == COL_DEF_SRC_PORT) ||
190                                         (col_fmt == COL_RES_SRC_PORT) || 
191                                         (col_fmt == COL_DEF_DST_PORT) ||
192                                         (col_fmt == COL_RES_DST_PORT)))) {
193
194       /* Compare numeric column */
195
196       if (num1 < num2)
197         return -1;
198       else if (num1 > num2)
199         return 1;
200       else
201         return COMPARE_FRAME_NUM();
202     }
203
204     else {
205
206       /* Compare text column */
207       if (!text2) {
208         if (text1)
209           return 1;
210         else
211           return COMPARE_FRAME_NUM();
212       }
213
214       if (!text1)
215         return -1;
216
217       ret = strcmp(text1, text2);
218       if (ret == 0)
219         return COMPARE_FRAME_NUM();
220       else
221         return ret;
222     }
223   }
224 }
225
226 /* What to do when a column is clicked */
227 static void
228 packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
229 {
230   column_arrows *col_arrows = (column_arrows *) data;
231   int i;
232
233   gtk_clist_freeze(clist);
234
235   for (i = 0; i < cfile.cinfo.num_cols; i++) {
236     gtk_widget_hide(col_arrows[i].ascend_pm);
237     gtk_widget_hide(col_arrows[i].descend_pm);
238   }
239
240   if (column == clist->sort_column) {
241     if (clist->sort_type == GTK_SORT_ASCENDING) {
242       clist->sort_type = GTK_SORT_DESCENDING;
243       gtk_widget_show(col_arrows[column].descend_pm);
244     } else {
245       clist->sort_type = GTK_SORT_ASCENDING;
246       gtk_widget_show(col_arrows[column].ascend_pm);
247     }
248   }
249   else {
250     clist->sort_type = GTK_SORT_ASCENDING;
251     gtk_widget_show(col_arrows[column].ascend_pm);
252     gtk_clist_set_sort_column(clist, column);
253   }
254   gtk_clist_thaw(clist);
255
256   gtk_clist_sort(clist);
257 }
258
259 static void
260 packet_list_resize_column_cb(GtkCList *clist _U_, gint column, gint width, gpointer data _U_)
261 {
262   recent_set_column_width (column, width);
263 }
264
265 /* What to do when a list item is selected/unselected */
266 static void
267 packet_list_select_cb(GtkWidget *w _U_, gint row, gint col _U_, GdkEventButton *event _U_, gpointer evt _U_) 
268 {
269   frame_data *fdata;
270
271   /* Check if already selected */
272   if (cfile.current_frame && cfile.current_row == row)
273     return;
274
275   /* Remove the hex display tabbed pages */
276   while( (gtk_notebook_get_nth_page( GTK_NOTEBOOK(byte_nb_ptr), 0)))
277     gtk_notebook_remove_page( GTK_NOTEBOOK(byte_nb_ptr), 0);
278
279   cf_select_packet(&cfile, row);
280   gtk_widget_grab_focus(packet_list);
281
282   /* Lookup the frame number that corresponds to the list row number */
283   fdata = (frame_data *)packet_list_get_row_data(row);
284   if (fdata != NULL) {
285     packet_history_add(fdata->num);
286   }
287 }
288
289 static void
290 packet_list_unselect_cb(GtkWidget *w _U_, gint row _U_, gint col _U_, GdkEventButton *event _U_, gpointer evt _U_) 
291 {
292   cf_unselect_packet(&cfile);
293 }
294
295 /* mark packets */
296 static void
297 set_frame_mark(gboolean set, frame_data *frame, gint row)
298 {
299   GdkColor fg, bg;
300
301   if (row == -1)
302     return;
303   if (set) {
304     cf_mark_frame(&cfile, frame);
305     color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg);
306     color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg);
307     gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
308     gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
309   } else {
310     color_filter_t *cfilter = frame->color_filter;
311
312     cf_unmark_frame(&cfile, frame);
313     /* Restore the color from the matching color filter if any */
314     if (cfilter) { /* The packet matches a color filter */
315       color_t_to_gdkcolor(&fg, &cfilter->fg_color);
316       color_t_to_gdkcolor(&bg, &cfilter->bg_color);
317       gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
318       gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
319     } else { /* No color filter match */
320       gtk_clist_set_foreground(GTK_CLIST(packet_list), row, NULL);
321       gtk_clist_set_background(GTK_CLIST(packet_list), row, NULL);
322     }
323   }
324 }
325
326 /* call this after last set_frame_mark is done */
327 static void mark_frames_ready(void) 
328 {
329   file_save_update_dynamics();
330   packets_bar_update();
331 }
332
333 void packet_list_mark_frame_cb(GtkWidget *w _U_, gpointer data _U_) 
334 {
335   if (cfile.current_frame) {
336     set_frame_mark(!cfile.current_frame->flags.marked,
337                    cfile.current_frame, cfile.current_row);
338     mark_frames_ready();
339   }
340 }
341
342 static void mark_all_frames(gboolean set) 
343 {
344   frame_data *fdata;
345
346   /* XXX: we might need a progressbar here */
347   for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
348     set_frame_mark(set,
349                    fdata,
350                    gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata));
351   }
352   mark_frames_ready();
353 }
354
355 void packet_list_update_marked_frames(void) 
356 {
357   frame_data *fdata;
358
359   if (cfile.plist == NULL) return;
360
361   /* XXX: we might need a progressbar here */
362   for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
363     if (fdata->flags.marked)
364       set_frame_mark(TRUE, fdata,
365                      gtk_clist_find_row_from_data(GTK_CLIST(packet_list),
366                                                   fdata));
367   }
368   mark_frames_ready();
369 }
370
371 void packet_list_mark_all_frames_cb(GtkWidget *w _U_, gpointer data _U_) 
372 {
373   mark_all_frames(TRUE);
374 }
375
376 void packet_list_unmark_all_frames_cb(GtkWidget *w _U_, gpointer data _U_) 
377 {
378   mark_all_frames(FALSE);
379 }
380
381 gboolean
382 packet_list_get_event_row_column(GtkWidget *w, GdkEventButton *event_button,
383                                  gint *row, gint *column)
384 {
385     return gtk_clist_get_selection_info(GTK_CLIST(w),
386                                  (gint) event_button->x, (gint) event_button->y,
387                                   row, column);
388 }
389
390 static gint
391 packet_list_button_pressed_cb(GtkWidget *w, GdkEvent *event, gpointer data _U_)
392 {
393     GdkEventButton *event_button = (GdkEventButton *)event;
394     gint row, column;
395
396     if (w == NULL || event == NULL)
397         return FALSE;
398
399     if (event->type == GDK_BUTTON_PRESS && event_button->button == 2 &&
400         event_button->window == GTK_CLIST(w)->clist_window &&
401         gtk_clist_get_selection_info(GTK_CLIST(w), (gint) event_button->x,
402                                      (gint) event_button->y, &row, &column)) {
403         frame_data *fdata = (frame_data *)gtk_clist_get_row_data(GTK_CLIST(w), row);
404         set_frame_mark(!fdata->flags.marked, fdata, row);
405         mark_frames_ready();
406         return TRUE;
407     }
408
409     if (event->type == GDK_2BUTTON_PRESS && event_button->button == 1 &&
410         event_button->window == GTK_CLIST(w)->clist_window ) {
411
412         new_window_cb(w);
413         return TRUE;
414     }
415
416     return FALSE;
417 }
418
419 /* Set the selection mode of the packet list window. */
420 void
421 packet_list_set_sel_browse(gboolean val, gboolean force_set)
422 {
423     GtkSelectionMode new_mode;
424     /* initialize with a mode we don't use, so that the mode == new_mode
425      * test will fail the first time */
426     static GtkSelectionMode mode = GTK_SELECTION_MULTIPLE;
427
428     /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I
429      * think "browse" in Wireshark makes more sense than "SINGLE" in GTK+ */
430     new_mode = val ? GTK_SELECTION_SINGLE : GTK_SELECTION_BROWSE;
431
432     if ((mode == new_mode) && !force_set) {
433         /*
434          * The mode isn't changing, so don't do anything.
435          * In particular, don't gratuitiously unselect the
436          * current packet.
437          *
438          * XXX - why do we have to unselect the current packet
439          * ourselves?  The documentation for the GtkCList at
440          *
441          *      http://developer.gnome.org/doc/API/gtk/gtkclist.html
442          *
443          * says "Note that setting the widget's selection mode to
444          * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will
445          * cause all the items in the GtkCList to become deselected."
446          */
447       return;
448     }
449
450     if (cfile.finfo_selected)
451         cf_unselect_field(&cfile);
452
453     mode = new_mode;
454     gtk_clist_set_selection_mode(GTK_CLIST(packet_list), mode);
455 }
456
457 /* Set the font of the packet list window. */
458 void
459 packet_list_set_font(PangoFontDescription *font)
460 {
461     int i;
462     gint col_width;
463     PangoLayout *layout;
464
465     /* Manually set the font so it can be used right away in the
466      * pango_layout_get_pixel_size call below.  The gtk_widget_modify_font
467      * function only takes effect after the widget is displayed. */
468     packet_list->style->font_desc = pango_font_description_copy(font);
469
470     gtk_widget_modify_font(packet_list, font);
471
472     /* Compute default column sizes. */
473     for (i = 0; i < cfile.cinfo.num_cols; i++) {
474         col_width = recent_get_column_width(i);
475         if (col_width == -1) {
476             layout = gtk_widget_create_pango_layout(packet_list, get_column_width_string(get_column_format(i), i));
477             pango_layout_get_pixel_size(layout, &col_width, NULL);
478             g_object_unref(G_OBJECT(layout));
479         }
480         gtk_clist_set_column_width(GTK_CLIST(packet_list), i, col_width);
481     }
482 }
483
484 GtkWidget *
485 packet_list_new(e_prefs *prefs)
486 {
487     GtkWidget *pkt_scrollw;
488     header_field_info *hfi;
489     gboolean custom_right_justify;
490     int      i;
491
492     /* Packet list */
493     pkt_scrollw = scrolled_window_new(NULL, NULL);
494     /* The usual policy for scrolled windows is to set both scrollbars to automatic,
495      * meaning they'll only appear if the content doesn't fit into the window.
496      *
497      * As this doesn't seem to work in some cases for the vertical scrollbar
498      * (see http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=220),
499      * we show that scrollbar always. */
500     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pkt_scrollw),
501                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
502     /* the gtk_clist will have it's own GTK_SHADOW_IN, so don't use a shadow
503      * for both widgets */
504     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(pkt_scrollw),
505                                     GTK_SHADOW_NONE);
506
507     packet_list = gtk_clist_new(cfile.cinfo.num_cols);
508     /* Column titles are filled in below */
509     gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
510
511     packet_list_set_sel_browse(prefs->gui_plist_sel_browse, FALSE);
512     packet_list_set_font(user_font_get_regular());
513     gtk_widget_set_name(packet_list, "packet list");
514     g_signal_connect(packet_list, "select-row", G_CALLBACK(packet_list_select_cb), NULL);
515     g_signal_connect(packet_list, "unselect-row", G_CALLBACK(packet_list_unselect_cb), NULL);
516     for (i = 0; i < cfile.cinfo.num_cols; i++) {
517         /* For performance reasons, columns do not automatically resize,
518            but are resizeable by the user. */
519         gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, FALSE);
520         gtk_clist_set_column_resizeable(GTK_CLIST(packet_list), i, TRUE);
521
522         custom_right_justify = FALSE;
523         if (cfile.cinfo.col_fmt[i] == COL_CUSTOM) {
524           hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[i]);
525           if ((hfi != NULL) && (hfi->strings == NULL) && 
526               ((hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
527                (((hfi->display == BASE_DEC) || (hfi->display == BASE_OCT)) &&
528                 (IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)  || 
529                  (hfi->type == FT_INT64) || (hfi->type == FT_UINT64))))) {
530             custom_right_justify = TRUE;
531           }
532         }
533
534         /* Right-justify some special columns. */
535         if (cfile.cinfo.col_fmt[i] == COL_NUMBER ||
536             cfile.cinfo.col_fmt[i] == COL_PACKET_LENGTH ||
537             cfile.cinfo.col_fmt[i] == COL_CUMULATIVE_BYTES ||
538             cfile.cinfo.col_fmt[i] == COL_DCE_CALL ||
539             cfile.cinfo.col_fmt[i] == COL_DCE_CTX ||
540             custom_right_justify)
541             gtk_clist_set_column_justification(GTK_CLIST(packet_list), i,
542                                                GTK_JUSTIFY_RIGHT);
543     }
544     g_signal_connect(packet_list, "button_press_event", G_CALLBACK(popup_menu_handler),
545                    g_object_get_data(G_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
546     g_signal_connect(packet_list, "button_press_event",
547                    G_CALLBACK(packet_list_button_pressed_cb), NULL);
548     g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, packet_list);
549     gtk_clist_set_compare_func(GTK_CLIST(packet_list), packet_list_compare);
550     gtk_widget_show(packet_list);
551
552     return pkt_scrollw;
553 }
554
555 void
556 packet_list_recreate(void)
557 {
558     gtk_widget_destroy(pkt_scrollw);
559
560     prefs.num_cols = g_list_length(prefs.col_list);
561
562     build_column_format_array(&cfile.cinfo, FALSE);
563
564     pkt_scrollw = packet_list_new(&prefs);
565     gtk_widget_show(pkt_scrollw);
566     packet_list_set_column_titles();
567     packet_list_set_sel_browse(prefs.gui_plist_sel_browse, TRUE);
568
569     main_widgets_rearrange();
570
571     if(cfile.state != FILE_CLOSED)
572        redissect_packets();
573 }
574
575 void
576 packet_list_set_column_titles(void)
577 {
578     GtkStyle      *win_style;
579     GdkPixmap     *ascend_pm, *descend_pm;
580     GdkBitmap     *ascend_bm, *descend_bm;
581     column_arrows *col_arrows;
582     int            i;
583     GtkWidget     *column_lb;
584
585     win_style = gtk_widget_get_style(top_level);
586     ascend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &ascend_bm,
587                                              &win_style->bg[GTK_STATE_NORMAL],
588                                              (gchar **) clist_ascend_xpm);
589     descend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &descend_bm,
590                                               &win_style->bg[GTK_STATE_NORMAL],
591                                               (gchar **) clist_descend_xpm);
592
593     col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) *
594                                             cfile.cinfo.num_cols);
595     for (i = 0; i < cfile.cinfo.num_cols; i++) {
596         col_arrows[i].table = gtk_table_new(2, 2, FALSE);
597         gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
598         column_lb = gtk_label_new(cfile.cinfo.col_title[i]);
599         gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2,
600                          GTK_SHRINK, GTK_SHRINK, 0, 0);
601         gtk_widget_show(column_lb);
602         col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
603         gtk_table_attach(GTK_TABLE(col_arrows[i].table),
604                          col_arrows[i].ascend_pm,
605                          1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
606         if (i == 0) {
607             gtk_widget_show(col_arrows[i].ascend_pm);
608         }
609         col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
610         gtk_table_attach(GTK_TABLE(col_arrows[i].table),
611                          col_arrows[i].descend_pm,
612                          1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
613         gtk_clist_set_column_widget(GTK_CLIST(packet_list), i,
614                                     col_arrows[i].table);
615         gtk_widget_show(col_arrows[i].table);
616     }
617     gtk_clist_column_titles_show(GTK_CLIST(packet_list));
618     g_signal_connect(packet_list, "click-column", G_CALLBACK(packet_list_click_column_cb), col_arrows);
619     g_signal_connect(packet_list, "resize-column", G_CALLBACK(packet_list_resize_column_cb), NULL);
620 }
621
622 void
623 packet_list_clear(void)
624 {
625     packet_history_clear();
626
627     gtk_clist_clear(GTK_CLIST(packet_list));
628     gtk_widget_queue_draw(packet_list);
629 }
630
631 void
632 packet_list_freeze(void)
633 {
634     gtk_clist_freeze(GTK_CLIST(packet_list));
635 }
636
637 static void
638 packet_list_resize_columns(void) 
639 {
640     int         i;
641     int         progbar_nextstep;
642     int         progbar_quantum;
643     gboolean    progbar_stop_flag;
644     GTimeVal    progbar_start_time;
645     float       progbar_val;
646     progdlg_t  *progbar = NULL;
647     gchar       status_str[100];
648
649     /* Update the progress bar when it gets to this value. */
650     progbar_nextstep = 0;
651     /* When we reach the value that triggers a progress bar update,
652        bump that value by this amount. */
653     progbar_quantum = cfile.cinfo.num_cols/N_PROGBAR_UPDATES;
654     /* Progress so far. */
655     progbar_val = 0.0f;
656
657     progbar_stop_flag = FALSE;
658     g_get_current_time(&progbar_start_time);
659
660
661     main_window_update();
662
663     for (i = 0; i < cfile.cinfo.num_cols; i++) {
664       /* Create the progress bar if necessary.
665          We check on every iteration of the loop, so that it takes no
666          longer than the standard time to create it (otherwise, for a
667          large file, we might take considerably longer than that standard
668          time in order to get to the next progress bar step). */
669       if (progbar == NULL)
670          progbar = delayed_create_progress_dlg("Resizing", "Resize Columns",
671            TRUE, &progbar_stop_flag, &progbar_start_time, progbar_val);
672
673       if (i >= progbar_nextstep) {
674         /* let's not divide by zero. I should never be started
675          * with count == 0, so let's assert that
676          */
677         g_assert(cfile.cinfo.num_cols > 0);
678
679         progbar_val = (gfloat) i / cfile.cinfo.num_cols;
680
681         if (progbar != NULL) {
682           g_snprintf(status_str, sizeof(status_str),
683                      "%u of %u columns (%s)", i+1, cfile.cinfo.num_cols, cfile.cinfo.col_title[i]);
684           update_progress_dlg(progbar, progbar_val, status_str);
685         }
686
687         progbar_nextstep += progbar_quantum;
688       }
689
690       if (progbar_stop_flag) {
691         /* Well, the user decided to abort the resizing... */
692         break;
693       }
694
695       /* auto resize the current column */
696       gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
697
698       /* the current column should be resizeable by the user again */
699       /* (will turn off auto resize again) */
700       gtk_clist_set_column_resizeable(GTK_CLIST(packet_list), i, TRUE);
701     }
702
703     /* We're done resizing the columns; destroy the progress bar if it
704        was created. */
705     if (progbar != NULL)
706       destroy_progress_dlg(progbar);
707 }
708
709 void packet_list_resize_columns_cb(GtkWidget *widget _U_, gpointer data _U_)
710 {
711     packet_list_resize_columns();
712 }
713
714 void
715 packet_list_thaw(void)
716 {
717     gtk_clist_thaw(GTK_CLIST(packet_list));
718     packets_bar_update();
719     /*packet_list_resize_columns();*/
720 }
721
722 void
723 packet_list_select_row(gint row)
724 {
725     g_signal_emit_by_name(G_OBJECT(packet_list), "select_row", row);
726 }
727
728 static void
729 packet_list_next_prev(gboolean next)
730 {
731     GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(top_level));
732     g_signal_emit_by_name(G_OBJECT(packet_list), "scroll_vertical",
733         next ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD, 0.0);
734     /* Set the focus back where it was */
735     if (focus)
736         gtk_window_set_focus(GTK_WINDOW(top_level), focus);
737 }
738
739 void
740 packet_list_next(void)
741 {
742     packet_list_next_prev(TRUE);
743 }
744
745 void
746 packet_list_prev(void)
747 {
748     packet_list_next_prev(FALSE);
749 }
750
751 void
752 packet_list_moveto_end(void)
753 {
754     gtk_clist_moveto(GTK_CLIST(packet_list),
755                      GTK_CLIST(packet_list)->rows - 1, -1, 1.0f, 1.0f);
756 }
757
758 gboolean
759 packet_list_check_end(void)
760 {
761     gboolean at_end = FALSE;
762     GtkAdjustment *adj;
763
764     g_return_val_if_fail (packet_list != NULL, FALSE);
765     adj = gtk_clist_get_vadjustment(GTK_CLIST(packet_list));
766     g_return_val_if_fail (adj != NULL, FALSE);
767
768     if (adj->value >= adj->upper - adj->page_size) {
769         at_end = TRUE;
770     }
771 #ifdef HAVE_LIBPCAP
772     if (adj->value > 0 && at_end != last_at_end && at_end != auto_scroll_live) {
773         menu_auto_scroll_live_changed(at_end);
774     }
775 #endif
776     last_at_end = at_end;
777     return at_end;
778 }
779
780 gint
781 packet_list_append(const gchar *text[], gpointer data)
782 {
783     gint row;
784
785     row = gtk_clist_append(GTK_CLIST(packet_list), (gchar **) text);
786     gtk_clist_set_row_data(GTK_CLIST(packet_list), row, data);
787     return row;
788 }
789
790 void
791 packet_list_set_colors(gint row, color_t *fg, color_t *bg)
792 {
793     GdkColor gdkfg, gdkbg;
794
795     if (fg)
796     {
797         color_t_to_gdkcolor(&gdkfg, fg);
798         gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &gdkfg);
799     }
800     if (bg)
801     {
802         color_t_to_gdkcolor(&gdkbg, bg);
803         gtk_clist_set_background(GTK_CLIST(packet_list), row, &gdkbg);
804     }
805 }
806
807 gint
808 packet_list_find_row_from_data(gpointer data)
809 {
810     return gtk_clist_find_row_from_data(GTK_CLIST(packet_list), data);
811 }
812
813 void
814 packet_list_set_text(gint row, gint column, const gchar *text)
815 {
816     gtk_clist_set_text(GTK_CLIST(packet_list), row, column, text);
817 }
818
819 /* Set the column widths of those columns that show the time in
820  * "command-line-specified" format. */
821 void
822 packet_list_set_time_width(gint col_fmt, gint column)
823 {
824     gint      width = -1;
825     PangoLayout  *layout;
826
827     width = recent_get_column_width(column);
828     if (width == -1) {
829         layout = gtk_widget_create_pango_layout(packet_list,
830                      get_column_longest_string(col_fmt));
831         pango_layout_get_pixel_size(layout, &width, NULL);
832         g_object_unref(G_OBJECT(layout));
833     }
834     gtk_clist_set_column_width(GTK_CLIST(packet_list), column, width);
835 }
836
837 gpointer
838 packet_list_get_row_data(gint row)
839 {
840     return gtk_clist_get_row_data(GTK_CLIST(packet_list), row);
841 }
842
843
844 /* get the first fully visible row number, given row MUST be visible */
845 static gint
846 packet_list_first_full_visible_row(gint row) 
847 {
848         g_assert(gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) == GTK_VISIBILITY_FULL);
849
850         while(gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) == GTK_VISIBILITY_FULL) {
851                 row--;
852         }
853
854         return ++row;
855 }
856
857 /* get the last fully visible row number, given row MUST be visible */
858 static gint
859 packet_list_last_full_visible_row(gint row) 
860 {
861         g_assert(gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) == GTK_VISIBILITY_FULL);
862
863         while(gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) == GTK_VISIBILITY_FULL) {
864                 row++;
865         }
866
867         return --row;
868 }
869
870 /* Set the selected row and the focus row of the packet list to the specified
871  * row, and make it visible if it's not currently visible. */
872 void
873 packet_list_set_selected_row(gint row)
874 {
875     gint visible_rows;
876     gint first_row;
877     gboolean full_visible;
878
879     full_visible = gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) ==  GTK_VISIBILITY_FULL;
880
881     /* XXX - why is there no "gtk_clist_set_focus_row()", so that we
882      * can make the row for the frame we found the focus row?
883      *
884      * See http://www.gnome.org/mailing-lists/archives/gtk-list/2000-January/0038.shtml
885      */
886     GTK_CLIST(packet_list)->focus_row = row;
887
888     gtk_clist_select_row(GTK_CLIST(packet_list), row, -1);
889
890     if (!full_visible) {
891         gtk_clist_freeze(GTK_CLIST(packet_list));
892
893         gtk_clist_moveto(GTK_CLIST(packet_list), row, -1, 0.0f, 0.0f);
894
895         /* even after move still invisible (happens with empty list) -> give up */
896         if(gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) != GTK_VISIBILITY_FULL) {
897             gtk_clist_thaw(GTK_CLIST(packet_list));
898             return;
899         }
900
901         /* The now selected row will be the first visible row in the list.
902          * This is inconvenient, as the user is usually interested in some
903          * packets *before* the currently selected one too.
904          *
905          * Try to adjust the visible rows, so the currently selected row will
906          * be shown around the first third of the list screen.
907          *
908          * (This won't even do any harm if the current row is the first or the
909          * last in the list) */
910         visible_rows = packet_list_last_full_visible_row(row) - packet_list_first_full_visible_row(row);
911         first_row = row - visible_rows / 3;
912
913         gtk_clist_moveto(GTK_CLIST(packet_list), first_row >= 0 ? first_row : 0, -1, 0.0f, 0.0f);
914
915         gtk_clist_thaw(GTK_CLIST(packet_list));
916     }
917 }
918
919 /* Return the column number that the clist is currently sorted by */
920 gint
921 packet_list_get_sort_column(void)
922 {
923     return GTK_CLIST(packet_list)->sort_column;
924 }
925
926 void packet_list_copy_summary_cb(GtkWidget * w _U_, gpointer data _U_, copy_summary_type copy_type)
927 {
928     gint col;
929     gchar* celltext = NULL;
930     GString* text;
931
932     if(CS_CSV == copy_type) {
933         text = g_string_new("\"");
934     } else {
935         text = g_string_new("");
936     }
937
938     if (cfile.current_frame) {
939         for(col = 0; col < cfile.cinfo.num_cols; ++col) {
940             if(col != 0) {
941                 if(CS_CSV == copy_type) {
942                     g_string_append(text,"\",\"");
943                 } else {
944                     g_string_append_c(text, '\t');
945                 }
946             }
947             if(0 != gtk_clist_get_text(GTK_CLIST(packet_list),cfile.current_row,col,&celltext)) {
948                 g_string_append(text,celltext);
949             }
950         }
951         if(CS_CSV == copy_type) {
952             g_string_append_c(text,'"');
953         }
954         copy_to_clipboard(text);
955     }
956     g_string_free(text,TRUE);
957 }
958
959 /* Re-sort the clist by the previously selected sort */
960 void
961 packet_list_set_sort_column(void)
962 {
963     packet_list_freeze();
964
965     gtk_clist_set_sort_column(GTK_CLIST(packet_list), packet_list_get_sort_column());
966
967     gtk_clist_sort(GTK_CLIST(packet_list));
968
969     packet_list_thaw();
970 }
971
972 void
973 packet_list_recent_write_all(FILE *rf)
974 {
975   gint col;
976
977   fprintf (rf, "%s:", RECENT_KEY_COL_WIDTH);
978   for (col = 0; col < cfile.cinfo.num_cols; col++) {
979      if (cfile.cinfo.col_fmt[col] == COL_CUSTOM) {
980        fprintf (rf, " %%Cus:%s,", get_column_custom_field(col));
981      } else {
982        fprintf (rf, " %s,", col_format_to_string(cfile.cinfo.col_fmt[col]));
983      }
984      fprintf (rf, " %d", GTK_CLIST(packet_list)->column[col].width);
985      if (col != cfile.cinfo.num_cols-1) {
986        fprintf (rf, ",");
987      }
988    }
989    fprintf (rf, "\n");
990 }