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