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