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