09c32525c492a0ea4edeafeb22f8b335dc5abf88
[metze/wireshark/wip.git] / ui / gtk / packet_list.c
1 /* packet_list.c
2  * Routines to implement a new GTK2 packet list using our custom model
3  * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
4  * Co-authors Anders Broman, Kovarththanan Rajaratnam and Stig Bjorlykke.
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
23  * USA.
24  */
25
26 #include "config.h"
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <gtk/gtk.h>
32
33 #include <epan/prefs.h>
34 #include <epan/packet.h>
35 #include <epan/column.h>
36 #include <epan/strutil.h>
37 #include <epan/plugin_if.h>
38
39 #include "ui/main_statusbar.h"
40 #include "ui/packet_list_utils.h"
41 #include "ui/preference_utils.h"
42 #include "ui/recent.h"
43 #include "ui/recent_utils.h"
44
45 #include "packet_list_store.h"
46 #include "ui/gtk/packet_list.h"
47 #include "ui/gtk/gtkglobals.h"
48 #include "ui/gtk/font_utils.h"
49 #include "ui/gtk/packet_history.h"
50 #include "ui/gtk/keys.h"
51 #include "ui/gtk/menus.h"
52 #include <epan/color_filters.h>
53 #include "ui/gtk/color_utils.h"
54 #include "ui/gtk/packet_win.h"
55 #include "ui/gtk/main.h"
56 #include "ui/gtk/dlg_utils.h"
57 #include "ui/gtk/filter_dlg.h"
58 #include "ui/gtk/filter_autocomplete.h"
59
60 #include "ui/gtk/old-gtk-compat.h"
61
62 #include <wsutil/str_util.h>
63
64 #define COLUMN_WIDTH_MIN 40
65 #define MAX_COMMENTS_TO_FETCH 20000000 /* Arbitrary */
66
67 #define COL_EDIT_COLUMN          "column"
68 #define COL_EDIT_FORMAT_CMB      "format_cmb"
69 #define COL_EDIT_TITLE_TE        "title_te"
70 #define COL_EDIT_FIELD_LB        "field_lb"
71 #define COL_EDIT_FIELD_TE        "field_te"
72 #define COL_EDIT_OCCURRENCE_LB   "occurrence_lb"
73 #define COL_EDIT_OCCURRENCE_TE   "occurrente_te"
74
75 static PacketList *packetlist;
76 static gboolean last_at_end = FALSE;
77 static gboolean enable_color;
78 static gulong column_changed_handler_id;
79
80 static GtkWidget *create_view_and_model(void);
81 static void scroll_to_and_select_iter(GtkTreeModel *model, GtkTreeSelection *selection, GtkTreeIter *iter);
82 static void packet_list_select_cb(GtkTreeView *tree_view, gpointer data _U_);
83 static void packet_list_double_click_cb(GtkTreeView *treeview,
84                                             GtkTreePath *path _U_,
85                                             GtkTreeViewColumn *col _U_,
86                                             gpointer userdata _U_);
87 static void show_cell_data_func(GtkTreeViewColumn *col,
88                                 GtkCellRenderer *renderer,
89                                 GtkTreeModel *model,
90                                 GtkTreeIter *iter,
91                                 gpointer data);
92 static gint row_number_from_iter(GtkTreeIter *iter);
93 static void scroll_to_current(void);
94 static gboolean query_packet_list_tooltip_cb(GtkWidget *widget, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip, gpointer data _U_);
95
96 GtkWidget *
97 packet_list_create(void)
98 {
99         GtkWidget *view, *scrollwin;
100
101         scrollwin = scrolled_window_new(NULL, NULL);
102
103         view = create_view_and_model();
104
105         gtk_container_add(GTK_CONTAINER(scrollwin), view);
106
107         g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, view);
108
109         return scrollwin;
110 }
111
112 /** @todo XXX - implement a smarter solution for recreating the packet list */
113 void
114 packet_list_recreate(void)
115 {
116         g_signal_handler_block(packetlist->view, column_changed_handler_id);
117         gtk_widget_destroy(pkt_scrollw);
118
119         prefs.num_cols = g_list_length(prefs.col_list);
120
121         col_cleanup(&cfile.cinfo);
122         build_column_format_array(&cfile.cinfo, prefs.num_cols, FALSE);
123
124         pkt_scrollw = packet_list_create();
125         gtk_widget_show_all(pkt_scrollw);
126
127         main_widgets_rearrange();
128
129         if(cfile.state != FILE_CLOSED)
130                 redissect_packets();
131 }
132
133 guint
134 packet_list_append(column_info *cinfo _U_, frame_data *fdata)
135 {
136         /* fdata should be filled with the stuff we need
137          * strings are built at display time.
138          */
139         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
140         guint visible_pos = packet_list_append_record(packetlist, fdata);
141         if(model)
142                 /* If the model is connected there is no packetsbar_update from "thaw */
143                 packets_bar_update();
144         /* Return the _visible_ position */
145
146         return visible_pos;
147 }
148
149 static void
150 col_title_change_ok (GtkWidget *w, gpointer parent_w)
151 {
152         GtkTreeViewColumn *col;
153         const gchar  *title, *name, *occurrence_text;
154         gint          col_id, cur_fmt, occurrence, col_width;
155         gchar        *escaped_title;
156         gboolean      recreate = FALSE;
157
158         col = (GtkTreeViewColumn *)g_object_get_data (G_OBJECT(w), COL_EDIT_COLUMN);
159         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY));
160
161         title = gtk_entry_get_text(GTK_ENTRY(g_object_get_data (G_OBJECT(w), COL_EDIT_TITLE_TE)));
162         name = gtk_entry_get_text(GTK_ENTRY(g_object_get_data (G_OBJECT(w), COL_EDIT_FIELD_TE)));
163         cur_fmt =  gtk_combo_box_get_active(GTK_COMBO_BOX(g_object_get_data (G_OBJECT(w), COL_EDIT_FORMAT_CMB)));
164         occurrence_text = gtk_entry_get_text(GTK_ENTRY(g_object_get_data (G_OBJECT(w), COL_EDIT_OCCURRENCE_TE)));
165         occurrence = (gint)strtol(occurrence_text, NULL, 10);
166
167         escaped_title = ws_strdup_escape_char(title, '_');
168         gtk_tree_view_column_set_title(col, escaped_title);
169         g_free(escaped_title);
170
171         if (strcmp (title, get_column_title(col_id)) != 0) {
172                 set_column_title (col_id, title);
173         }
174
175         if (cur_fmt != get_column_format(col_id)) {
176                 set_column_format (col_id, cur_fmt);
177                 recreate = TRUE;
178         }
179
180         if (cur_fmt == COL_CUSTOM) {
181                 const gchar *custom_fields = get_column_custom_fields(col_id);
182                 if ((custom_fields && strcmp (name, custom_fields) != 0) || (custom_fields == NULL)) {
183                         set_column_custom_fields(col_id, name);
184                         recreate = TRUE;
185                 }
186
187                 if (occurrence != get_column_custom_occurrence(col_id)) {
188                         set_column_custom_occurrence (col_id, occurrence);
189                         recreate = TRUE;
190                 }
191         }
192
193         col_width = get_default_col_size (packetlist->view, title);
194         gtk_tree_view_column_set_min_width(col, col_width);
195
196         if (!prefs.gui_use_pref_save) {
197                 prefs_main_write();
198         }
199
200         rebuild_visible_columns_menu ();
201
202         if (recreate) {
203                 packet_list_recreate();
204         }
205
206         packet_list_resize_column (col_id);
207         window_destroy(GTK_WIDGET(parent_w));
208 }
209
210 static void
211 col_title_change_cancel (GtkWidget *w _U_, gpointer parent_w)
212 {
213         window_destroy(GTK_WIDGET(parent_w));
214 }
215
216 static void
217 col_details_format_changed_cb(GtkWidget *w, gpointer data _U_)
218 {
219         GtkWidget *field_lb, *field_te, *occurrence_lb, *occurrence_te;
220         gint       cur_fmt;
221
222         field_lb = (GtkWidget *)g_object_get_data (G_OBJECT(w), COL_EDIT_FIELD_LB);
223         field_te = (GtkWidget *)g_object_get_data (G_OBJECT(w), COL_EDIT_FIELD_TE);
224         occurrence_lb = (GtkWidget *)g_object_get_data (G_OBJECT(w), COL_EDIT_OCCURRENCE_LB);
225         occurrence_te = (GtkWidget *)g_object_get_data (G_OBJECT(w), COL_EDIT_OCCURRENCE_TE);
226
227         cur_fmt = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
228
229         if (cur_fmt == COL_CUSTOM) {
230                 /* Changing to custom */
231                 gtk_widget_set_sensitive(field_lb, TRUE);
232                 gtk_widget_set_sensitive(field_te, TRUE);
233                 gtk_widget_set_sensitive(occurrence_lb, TRUE);
234                 gtk_widget_set_sensitive(occurrence_te, TRUE);
235         } else {
236                 /* Changing to non-custom */
237                 gtk_widget_set_sensitive(field_lb, FALSE);
238                 gtk_widget_set_sensitive(field_te, FALSE);
239                 gtk_widget_set_sensitive(occurrence_lb, FALSE);
240                 gtk_widget_set_sensitive(occurrence_te, FALSE);
241         }
242 }
243
244 static void
245 col_details_edit_dlg (gint col_id, GtkTreeViewColumn *col)
246 {
247         const gchar *title = gtk_tree_view_column_get_title(col);
248         gchar *unescaped_title = ws_strdup_unescape_char(title, '_');
249
250         GtkWidget *label, *field_lb, *occurrence_lb;
251         GtkWidget *title_te, *format_cmb, *field_te, *occurrence_te;
252         GtkWidget *win, *main_grid, *main_vb, *bbox, *cancel_bt, *ok_bt;
253         char       custom_occurrence_str[8];
254         gint       cur_fmt, i;
255
256         win = dlg_window_new("Wireshark: Edit Column Details");
257
258         gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
259         gtk_window_resize(GTK_WINDOW(win), 400, 100);
260
261         main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 6, FALSE);
262         gtk_container_add(GTK_CONTAINER(win), main_vb);
263         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
264
265         main_grid = ws_gtk_grid_new();
266         gtk_box_pack_start(GTK_BOX(main_vb), main_grid, FALSE, FALSE, 0);
267         ws_gtk_grid_set_column_spacing(GTK_GRID(main_grid), 10);
268         ws_gtk_grid_set_row_spacing(GTK_GRID(main_grid), 5);
269
270         label = gtk_label_new("Title:");
271         ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), label, 0, 0, 1, 1);
272         gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
273         gtk_widget_set_tooltip_text(label, "Packet list column title.");
274
275         title_te = gtk_entry_new();
276         ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), title_te, 1, 0, 1, 1);
277         gtk_entry_set_text(GTK_ENTRY(title_te), unescaped_title);
278         g_free(unescaped_title);
279         gtk_widget_set_tooltip_text(title_te, "Packet list column title.");
280
281         label = gtk_label_new("Field type:");
282         ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), label, 0, 1, 1, 1);
283         gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
284         gtk_widget_set_tooltip_text(label, "Select which packet information to present in the column.");
285
286         format_cmb = gtk_combo_box_text_new();
287         for (i = 0; i < NUM_COL_FMTS; i++) {
288            gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(format_cmb), col_format_desc(i));
289         }
290         g_signal_connect(format_cmb, "changed", G_CALLBACK(col_details_format_changed_cb), NULL);
291         ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), format_cmb, 1, 1, 1, 1);
292         gtk_widget_set_tooltip_text(format_cmb, "Select which packet information to present in the column.");
293
294         field_lb = gtk_label_new("Field name:");
295         ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), field_lb, 0, 2, 1, 1);
296         gtk_misc_set_alignment(GTK_MISC(field_lb), 1.0f, 0.5f);
297         gtk_widget_set_tooltip_text(field_lb,
298                               "Field name used when field type is \"Custom\". "
299                               "This string has the same syntax as a display filter string.");
300         field_te = gtk_entry_new();
301         ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), field_te, 1, 2, 1, 1);
302         g_object_set_data (G_OBJECT(field_te), E_FILT_MULTI_FIELD_NAME_ONLY_KEY, (gpointer)"");
303         g_signal_connect(field_te, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
304         g_signal_connect(field_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
305         g_signal_connect(win, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
306         gtk_widget_set_tooltip_text(field_te,
307                               "Field names used when field type is \"Custom\". "
308                               "This string has the same syntax as a display filter string.");
309
310         occurrence_lb = gtk_label_new("Occurrence:");
311         ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), occurrence_lb, 0, 3, 1, 1);
312         gtk_misc_set_alignment(GTK_MISC(occurrence_lb), 1.0f, 0.5f);
313         gtk_widget_set_tooltip_text (occurrence_lb,
314                               "Field occurrence to use. "
315                               "0=all (default), 1=first, 2=second, ..., -1=last.");
316
317         occurrence_te = gtk_entry_new();
318         gtk_entry_set_max_length (GTK_ENTRY(occurrence_te), 4);
319         ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), occurrence_te, 1, 3, 1, 1);
320         gtk_widget_set_tooltip_text (occurrence_te,
321                               "Field occurrence to use. "
322                               "0=all (default), 1=first, 2=second, ..., -1=last.");
323
324         bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
325         gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
326
327         ok_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
328         g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_COLUMN, col);
329         g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_FORMAT_CMB, format_cmb);
330         g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_TITLE_TE, title_te);
331         g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_FIELD_TE, field_te);
332         g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_OCCURRENCE_TE, occurrence_te);
333         g_object_set_data (G_OBJECT(format_cmb), COL_EDIT_FIELD_LB, field_lb);
334         g_object_set_data (G_OBJECT(format_cmb), COL_EDIT_FIELD_TE, field_te);
335         g_object_set_data (G_OBJECT(format_cmb), COL_EDIT_OCCURRENCE_LB, occurrence_lb);
336         g_object_set_data (G_OBJECT(format_cmb), COL_EDIT_OCCURRENCE_TE, occurrence_te);
337         g_signal_connect(ok_bt, "clicked", G_CALLBACK(col_title_change_ok), win);
338
339         cur_fmt = get_column_format (col_id);
340         gtk_combo_box_set_active(GTK_COMBO_BOX(format_cmb), cur_fmt);
341         if (cur_fmt == COL_CUSTOM) {
342                 gtk_entry_set_text(GTK_ENTRY(field_te), get_column_custom_fields(col_id));
343                 g_snprintf(custom_occurrence_str, sizeof(custom_occurrence_str), "%d", get_column_custom_occurrence(col_id));
344                 gtk_entry_set_text(GTK_ENTRY(occurrence_te), custom_occurrence_str);
345         }
346
347         dlg_set_activate(title_te, ok_bt);
348         dlg_set_activate(field_te, ok_bt);
349         dlg_set_activate(occurrence_te, ok_bt);
350
351         cancel_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
352         g_signal_connect(cancel_bt, "clicked", G_CALLBACK(col_title_change_cancel), win);
353         window_set_cancel_button(win, cancel_bt, NULL);
354
355         gtk_widget_grab_default(ok_bt);
356         gtk_widget_show_all(win);
357 }
358
359 /* Process sort request;
360  *   order:          GTK_SORT_ASCENDING or GTK_SORT_DESCENDING
361  *   sort_indicator: TRUE: set sort_indicator on column; FALSE: don't set ....
362  *
363  * If necessary, columns are first "columnized" for all rows in the packet-list; If this
364  *  is not completed (i.e., stopped), then the sort request is aborted.
365  */
366 static void
367 packet_list_sort_column (gint col_id, GtkTreeViewColumn *col, GtkSortType order, gboolean sort_indicator)
368 {
369         GtkTreeViewColumn *prev_col;
370
371         if (col == NULL) {
372                 col = gtk_tree_view_get_column(GTK_TREE_VIEW(packetlist->view), col_id);
373         }
374         g_assert(col);
375
376         if (!packet_list_do_packet_list_dissect_and_cache_all(packetlist, col_id)) {
377                 return;  /* "stopped": do not try to sort */
378         }
379
380         prev_col = (GtkTreeViewColumn *)
381           g_object_get_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_PREV_COLUMN_KEY);
382
383         if (prev_col) {
384                 gtk_tree_view_column_set_sort_indicator(prev_col, FALSE);
385         }
386         gtk_tree_view_column_set_sort_indicator(col, sort_indicator);
387         gtk_tree_view_column_set_sort_order (col, order);
388         g_object_set_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_PREV_COLUMN_KEY, col);
389         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(packetlist), col_id, order); /* triggers sort callback */
390
391         scroll_to_current ();
392 }
393
394 /*
395  * We have our own functionality to toggle sort order on a column to avoid
396  * having empty sorting arrow widgets in the column header.
397  */
398 static void
399 packet_list_column_clicked_cb (GtkTreeViewColumn *col, gpointer user_data _U_)
400 {
401         GtkSortType order = gtk_tree_view_column_get_sort_order (col);
402         gint col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY));
403
404         if (cfile.state == FILE_READ_IN_PROGRESS)
405                 return;
406
407         if (!gtk_tree_view_column_get_sort_indicator(col)) {
408                 packet_list_sort_column (col_id, col, GTK_SORT_ASCENDING, TRUE);
409         } else if (order == GTK_SORT_ASCENDING) {
410                 packet_list_sort_column (col_id, col, GTK_SORT_DESCENDING, TRUE);
411         } else {
412                 packet_list_sort_column (0, NULL, GTK_SORT_ASCENDING, FALSE);
413         }
414 }
415
416 static gdouble
417 get_xalign_value (gchar xalign, gboolean right_justify)
418 {
419         double value;
420
421         switch (xalign) {
422         case COLUMN_XALIGN_RIGHT:
423                 value = 1.0f;
424                 break;
425         case COLUMN_XALIGN_CENTER:
426                 value = 0.5f;
427                 break;
428         case COLUMN_XALIGN_LEFT:
429                 value = 0.0f;
430                 break;
431         case COLUMN_XALIGN_DEFAULT:
432         default:
433                 if (right_justify) {
434                         value = 1.0f;
435                 } else {
436                         value = 0.0f;
437                 }
438                 break;
439         }
440
441         return value;
442 }
443
444 static void
445 packet_list_xalign_column (gint col_id, GtkTreeViewColumn *col, gchar xalign)
446 {
447         GList *renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
448         gboolean right_justify = right_justify_column(col_id, &cfile);
449         gdouble value = get_xalign_value (xalign, right_justify);
450         GList *entry;
451         GtkCellRenderer *renderer;
452
453         entry = g_list_first(renderers);
454         while (entry) {
455                 renderer = (GtkCellRenderer *)entry->data;
456                 g_object_set(G_OBJECT(renderer), "xalign", value, NULL);
457                 entry = g_list_next (entry);
458         }
459         g_list_free (renderers);
460
461         if ((xalign == COLUMN_XALIGN_LEFT && !right_justify) ||
462             (xalign == COLUMN_XALIGN_RIGHT && right_justify)) {
463                 /* Default value selected, save default in the recent settings */
464                 xalign = COLUMN_XALIGN_DEFAULT;
465         }
466
467         recent_set_column_xalign (col_id, xalign);
468         gtk_widget_queue_draw (packetlist->view);
469 }
470
471 static void
472 packet_list_set_visible_column (gint col_id, GtkTreeViewColumn *col, gboolean visible)
473 {
474         gtk_tree_view_column_set_visible(col, visible);
475         set_column_visible(col_id, visible);
476
477         if (!prefs.gui_use_pref_save) {
478                 prefs_main_write();
479         }
480
481         rebuild_visible_columns_menu ();
482         gtk_widget_queue_draw (packetlist->view);
483 }
484
485 void
486 packet_list_toggle_visible_column (gint col_id)
487 {
488         GtkTreeViewColumn *col =
489           gtk_tree_view_get_column(GTK_TREE_VIEW(packetlist->view), col_id);
490
491         packet_list_set_visible_column (col_id, col, get_column_visible(col_id) ? FALSE : TRUE);
492 }
493
494 void
495 packet_list_set_all_columns_visible (void)
496 {
497         GtkTreeViewColumn *col;
498         int col_id;
499
500         for (col_id = 0; col_id < cfile.cinfo.num_cols; col_id++) {
501                 col = gtk_tree_view_get_column(GTK_TREE_VIEW(packetlist->view), col_id);
502                 gtk_tree_view_column_set_visible(col, TRUE);
503                 set_column_visible(col_id, TRUE);
504         }
505
506         if (!prefs.gui_use_pref_save) {
507                 prefs_main_write();
508         }
509
510         rebuild_visible_columns_menu ();
511         gtk_widget_queue_draw (packetlist->view);
512 }
513
514 static void
515 packet_list_remove_column (gint col_id, GtkTreeViewColumn *col _U_)
516 {
517         column_prefs_remove_nth(col_id);
518
519         if (!prefs.gui_use_pref_save) {
520                 prefs_main_write();
521         }
522
523         packet_list_recreate();
524 }
525
526 static void
527 packet_list_toggle_resolved (GtkWidget *w, gint col_id)
528 {
529         /* We have to check for skip-update because we get an emit in menus_set_column_resolved() */
530         if (g_object_get_data(G_OBJECT(w), "skip-update") == NULL) {
531                 set_column_resolved (col_id, get_column_resolved (col_id) ? FALSE : TRUE);
532
533                 if (!prefs.gui_use_pref_save) {
534                         prefs_main_write();
535                 }
536
537                 packet_list_recreate();
538         }
539 }
540
541 void
542 packet_list_column_menu_cb (GtkWidget *w, gpointer user_data _U_, COLUMN_SELECTED_E action)
543 {
544         GtkTreeViewColumn *col = (GtkTreeViewColumn *)
545           g_object_get_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_COLUMN_KEY);
546         gint col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY));
547
548         switch (action) {
549         case COLUMN_SELECTED_SORT_ASCENDING:
550                 packet_list_sort_column (col_id, col, GTK_SORT_ASCENDING, TRUE);
551                 break;
552         case COLUMN_SELECTED_SORT_DESCENDING:
553                 packet_list_sort_column (col_id, col, GTK_SORT_DESCENDING, TRUE);
554                 break;
555         case COLUMN_SELECTED_SORT_NONE:
556                 packet_list_sort_column (0, NULL, GTK_SORT_ASCENDING, FALSE);
557                 break;
558         case COLUMN_SELECTED_TOGGLE_RESOLVED:
559                 packet_list_toggle_resolved (w, col_id);
560                 break;
561         case COLUMN_SELECTED_ALIGN_LEFT:
562                 packet_list_xalign_column (col_id, col, COLUMN_XALIGN_LEFT);
563                 break;
564         case COLUMN_SELECTED_ALIGN_CENTER:
565                 packet_list_xalign_column (col_id, col, COLUMN_XALIGN_CENTER);
566                 break;
567         case COLUMN_SELECTED_ALIGN_RIGHT:
568                 packet_list_xalign_column (col_id, col, COLUMN_XALIGN_RIGHT);
569                 break;
570         case COLUMN_SELECTED_ALIGN_DEFAULT:
571                 packet_list_xalign_column (col_id, col, COLUMN_XALIGN_DEFAULT);
572                 break;
573         case COLUMN_SELECTED_RESIZE:
574                 packet_list_resize_column (col_id);
575                 break;
576         case COLUMN_SELECTED_CHANGE:
577                 col_details_edit_dlg (col_id, col);
578                 break;
579         case COLUMN_SELECTED_HIDE:
580                 packet_list_set_visible_column (col_id, col, FALSE);
581                 break;
582         case COLUMN_SELECTED_REMOVE:
583                 packet_list_remove_column (col_id, col);
584                 break;
585         default:
586                 g_assert_not_reached();
587                 break;
588         }
589 }
590
591 static gboolean
592 packet_list_column_button_pressed_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
593 {
594         GtkWidget *col = (GtkWidget *) data;
595         GtkWidget *menu = (GtkWidget *)g_object_get_data(G_OBJECT(popup_menu_object), PM_PACKET_LIST_COL_KEY);
596         gint       col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY));
597         gboolean   right_justify = right_justify_column (col_id, &cfile);
598
599         menus_set_column_align_default (right_justify);
600         menus_set_column_resolved (get_column_resolved (col_id), resolve_column (col_id, &cfile));
601         g_object_set_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_COLUMN_KEY, col);
602         return popup_menu_handler (widget, event, menu);
603 }
604
605 static gboolean packet_list_recreate_delayed(gpointer user_data _U_)
606 {
607         packet_list_recreate();
608         return FALSE;
609 }
610
611 static void
612 column_dnd_changed_cb(GtkTreeView *tree_view, gpointer data _U_)
613 {
614         GtkTreeViewColumn  *column;
615         GtkTreeSelection   *selection;
616         GtkTreeModel  *model;
617         GtkTreeIter    iter;
618         GList         *columns, *list, *clp, *new_col_list = NULL;
619         gint           old_col_id, new_col_id = 0;
620         fmt_data      *cfmt;
621
622         selection = gtk_tree_view_get_selection(tree_view);
623         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
624                 return;
625
626         list = columns = gtk_tree_view_get_columns(tree_view);
627         while (columns) {
628                 column = (GtkTreeViewColumn *)columns->data;
629                 old_col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), E_MPACKET_LIST_COL_KEY));
630
631                 clp = g_list_nth (prefs.col_list, old_col_id);
632                 cfmt = (fmt_data *) clp->data;
633                 new_col_list = g_list_append (new_col_list, cfmt);
634                 columns = g_list_next (columns);
635                 new_col_id++;
636         }
637         g_list_free (list);
638         g_list_free (prefs.col_list);
639
640         prefs.col_list = new_col_list;
641
642         if (!prefs.gui_use_pref_save) {
643                 prefs_main_write();
644         }
645
646         /* The columns widget is part of the packets list, delay destruction to
647          * avoid triggering a use-after-free (maybe a GTK3 bug?) */
648         g_idle_add(packet_list_recreate_delayed, NULL);
649 }
650
651 static GtkWidget *
652 create_view_and_model(void)
653 {
654         GtkTreeViewColumn *col;
655         GtkCellRenderer *renderer;
656         gint i, col_width;
657         gdouble value;
658         gchar *tooltip_text;
659         gint col_min_width;
660         gchar *escaped_title;
661         col_item_t* col_item;
662
663         packetlist = packet_list_new();
664
665         packetlist->view = tree_view_new(GTK_TREE_MODEL(packetlist));
666
667         gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(packetlist->view),
668                                                 TRUE);
669         g_signal_connect(packetlist->view, "cursor-changed",
670                          G_CALLBACK(packet_list_select_cb), NULL);
671         g_signal_connect(packetlist->view, "row-activated",
672                          G_CALLBACK(packet_list_double_click_cb), NULL);
673         g_signal_connect(packetlist->view, "button_press_event", G_CALLBACK(popup_menu_handler),
674                                    g_object_get_data(G_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
675         column_changed_handler_id = g_signal_connect(packetlist->view, "columns-changed", G_CALLBACK(column_dnd_changed_cb), NULL);
676         g_object_set(packetlist->view, "has-tooltip", TRUE, NULL);
677         g_signal_connect(packetlist->view, "query-tooltip",
678                         G_CALLBACK(query_packet_list_tooltip_cb), NULL);
679         g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, packetlist);
680
681         /*              g_object_unref(packetlist); */ /* Destroy automatically with view for now */ /* XXX - Messes up freezing & thawing */
682
683 #if GTK_CHECK_VERSION(3,0,0)
684         gtk_widget_override_font(packetlist->view, user_font_get_regular());
685 #else
686         gtk_widget_modify_font(packetlist->view, user_font_get_regular());
687 #endif
688
689         /* We need one extra column to store the entire PacketListRecord */
690         for(i = 0; i < cfile.cinfo.num_cols; i++) {
691                 col_item = &cfile.cinfo.columns[i];
692                 renderer = gtk_cell_renderer_text_new();
693                 col = gtk_tree_view_column_new();
694                 gtk_tree_view_column_pack_start(col, renderer, TRUE);
695                 value = get_xalign_value(recent_get_column_xalign(i), right_justify_column(i, &cfile));
696                 g_object_set(G_OBJECT(renderer),
697                              "xalign", value,
698                              NULL);
699                 g_object_set(renderer,
700                              "ypad", 0,
701                              NULL);
702                 gtk_tree_view_column_add_attribute(col, renderer, "text", i);
703                 gtk_tree_view_column_set_cell_data_func(col, renderer,
704                                                         show_cell_data_func,
705                                                         GINT_TO_POINTER(i),
706                                                         NULL);
707
708                 escaped_title = ws_strdup_escape_char(col_item->col_title, '_');
709                 gtk_tree_view_column_set_title(col, escaped_title);
710                 g_free (escaped_title);
711                 gtk_tree_view_column_set_clickable(col, TRUE);
712                 gtk_tree_view_column_set_resizable(col, TRUE);
713                 gtk_tree_view_column_set_visible(col, get_column_visible(i));
714                 gtk_tree_view_column_set_sizing(col,GTK_TREE_VIEW_COLUMN_FIXED);
715                 gtk_tree_view_column_set_reorderable(col, TRUE); /* XXX - Should this be saved in the prefs? */
716
717                 g_object_set_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY, GINT_TO_POINTER(i));
718                 g_signal_connect(col, "clicked", G_CALLBACK(packet_list_column_clicked_cb), NULL);
719
720                 /*
721                  * The column can't be adjusted to a size smaller than this
722                  * XXX The minimum size will be the size of the title
723                  * should that be limited for long titles?
724                  */
725                 col_min_width = get_default_col_size (packetlist->view, cfile.cinfo.columns[i].col_title);
726                 if(col_min_width<COLUMN_WIDTH_MIN){
727                         gtk_tree_view_column_set_min_width(col, COLUMN_WIDTH_MIN);
728                 }else{
729                         gtk_tree_view_column_set_min_width(col, col_min_width);
730                 }
731
732                 /* Set the size the column will be displayed with */
733                 col_width = recent_get_column_width(i);
734                 if(col_width < 1) {
735                         gint fmt;
736                         const gchar *long_str;
737
738                         fmt = get_column_format(i);
739                         long_str = get_column_width_string(fmt, i);
740                         if(long_str){
741                                 col_width = get_default_col_size (packetlist->view, long_str);
742                         }else{
743                                 col_width = COLUMN_WIDTH_MIN;
744                         }
745                         gtk_tree_view_column_set_fixed_width(col, col_width);
746                 }else{
747                         gtk_tree_view_column_set_fixed_width(col, col_width);
748                 }
749
750                 gtk_tree_view_append_column(GTK_TREE_VIEW(packetlist->view), col);
751
752                 tooltip_text = get_column_tooltip(i);
753                 gtk_widget_set_tooltip_text(gtk_tree_view_column_get_button(col), tooltip_text);
754                 g_free(tooltip_text);
755                 g_signal_connect(gtk_tree_view_column_get_button(col), "button_press_event",
756                                  G_CALLBACK(packet_list_column_button_pressed_cb), col);
757
758                 if (i == 0) {  /* Default sort on first column */
759                         g_object_set_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_COLUMN_KEY, col);
760                         g_object_set_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_PREV_COLUMN_KEY, col);
761                 }
762         }
763
764         rebuild_visible_columns_menu ();
765
766         return packetlist->view;
767 }
768
769 static frame_data *
770 packet_list_get_record(GtkTreeModel *model, GtkTreeIter *iter)
771 {
772         frame_data *fdata;
773         /* The last column is reserved for frame_data */
774         gint record_column = gtk_tree_model_get_n_columns(model)-1;
775
776         gtk_tree_model_get(model, iter,
777                            record_column,
778                            &fdata,
779                            -1);
780
781         return fdata;
782 }
783
784 void
785 packet_list_clear(void)
786 {
787         packet_history_clear();
788
789         packet_list_store_clear(packetlist);
790         gtk_widget_queue_draw(packetlist->view);
791         /* XXX is this correct in all cases?
792          * Reset the sort column, use packetlist as model in case the list is frozen.
793          */
794         packet_list_sort_column(0, NULL, GTK_SORT_ASCENDING, FALSE);
795 }
796
797 void
798 packet_list_freeze(void)
799 {
800         /* So we don't lose the model by the time we want to thaw it */
801         g_object_ref(packetlist);
802
803         /* Detach view from model */
804         gtk_tree_view_set_model(GTK_TREE_VIEW(packetlist->view), NULL);
805 }
806
807 void
808 packet_list_thaw(void)
809 {
810         /* Apply model */
811         gtk_tree_view_set_model( GTK_TREE_VIEW(packetlist->view), GTK_TREE_MODEL(packetlist));
812
813         /* Remove extra reference added by packet_list_freeze() */
814         g_object_unref(packetlist);
815
816         packets_bar_update();
817 }
818
819 void
820 packet_list_recreate_visible_rows(void)
821 {
822         packet_list_recreate_visible_rows_list(packetlist);
823 }
824
825 void packet_list_resize_column(gint col)
826 {
827         GtkTreeViewColumn *column;
828         gint col_width;
829         const gchar *long_str;
830
831         long_str = packet_list_get_widest_column_string(packetlist, col);
832         if(!long_str || strcmp("",long_str)==0)
833                 /* If we get an empty string leave the width unchanged */
834                 return;
835         column = gtk_tree_view_get_column (GTK_TREE_VIEW(packetlist->view), col);
836         col_width = get_default_col_size (packetlist->view, long_str);
837         gtk_tree_view_column_set_fixed_width(column, col_width);
838 }
839
840 static void
841 packet_list_resize_columns(void)
842 {
843         gint            progbar_loop_max;
844         gint            progbar_loop_var;
845
846         progbar_loop_max = cfile.cinfo.num_cols;
847
848         for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var)
849                 packet_list_resize_column(progbar_loop_var);
850 }
851
852 void
853 packet_list_resize_columns_cb(GtkWidget *widget _U_, gpointer data _U_)
854 {
855         packet_list_resize_columns();
856 }
857
858 static void
859 scroll_to_current(void)
860 {
861         GtkTreeSelection *selection;
862         GtkTreeIter iter;
863         GtkTreeModel *model;
864         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(top_level));
865
866         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
867         /* model is filled with the current model as a convenience. */
868         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
869                 return;
870
871         scroll_to_and_select_iter(model, selection, &iter);
872
873         /* Set the focus back where it was */
874         if (focus)
875                 gtk_window_set_focus(GTK_WINDOW(top_level), focus);
876 }
877
878 void
879 packet_list_next(void)
880 {
881         GtkTreeSelection *selection;
882         GtkTreeIter iter;
883         GtkTreeModel *model;
884         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(top_level));
885
886         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
887         /* model is filled with the current model as a convenience. */
888         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
889                 return;
890
891         if (!gtk_tree_model_iter_next(model, &iter))
892                 return;
893
894         scroll_to_and_select_iter(model, selection, &iter);
895
896         /* Set the focus back where it was */
897         if (focus)
898                 gtk_window_set_focus(GTK_WINDOW(top_level), focus);
899 }
900
901 void
902 packet_list_prev(void)
903 {
904         GtkTreeSelection *selection;
905         GtkTreeIter iter;
906         GtkTreeModel *model;
907         GtkTreePath *path;
908         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(top_level));
909
910         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
911         /* model is filled with the current model as a convenience. */
912         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
913                 return;
914
915         path = gtk_tree_model_get_path(model, &iter);
916
917         if (!gtk_tree_path_prev(path))
918                 return;
919
920         if (!gtk_tree_model_get_iter(model, &iter, path))
921                 return;
922
923         scroll_to_and_select_iter(model, selection, &iter);
924
925         gtk_tree_path_free(path);
926
927         /* Set the focus back where it was */
928         if (focus)
929                 gtk_window_set_focus(GTK_WINDOW(top_level), focus);
930 }
931
932 static void
933 scroll_to_and_select_iter(GtkTreeModel *model, GtkTreeSelection *selection, GtkTreeIter *iter)
934 {
935         GtkTreePath *path;
936
937         g_assert(model);
938
939         /* Select the row */
940         if(!selection)
941                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
942         gtk_tree_selection_select_iter (selection, iter);
943         path = gtk_tree_model_get_path(model, iter);
944         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(packetlist->view),
945                         path,
946                         NULL,
947                         TRUE,   /* use_align */
948                         0.5f,   /* row_align determines where the row is placed, 0.5 means center */
949                         0);     /* The horizontal alignment of the column */
950
951         /* "cursor-changed" signal triggers packet_list_select_cb() */
952         /*  which will update the middle and bottom panes.              */
953         gtk_tree_view_set_cursor(GTK_TREE_VIEW(packetlist->view),
954                         path,
955                         NULL,
956                         FALSE); /* start_editing */
957
958         gtk_tree_path_free(path);
959 }
960
961 void
962 packet_list_select_first_row(void)
963 {
964         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
965         GtkTreeIter iter;
966
967         if(!gtk_tree_model_get_iter_first(model, &iter))
968                 return;
969
970         scroll_to_and_select_iter(model, NULL, &iter);
971         gtk_widget_grab_focus(packetlist->view);
972 }
973
974 void
975 packet_list_select_last_row(void)
976 {
977         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
978         GtkTreeIter iter;
979         gint children;
980         guint last_row;
981
982         if((children = gtk_tree_model_iter_n_children(model, NULL)) == 0)
983                 return;
984
985         last_row = children-1;
986         if(!gtk_tree_model_iter_nth_child(model, &iter, NULL, last_row))
987                 return;
988
989         scroll_to_and_select_iter(model, NULL, &iter);
990 }
991
992 void
993 packet_list_moveto_end(void)
994 {
995         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
996         GtkTreeIter iter;
997         GtkTreePath *path;
998         gint children;
999         guint last_row;
1000
1001         if((children = gtk_tree_model_iter_n_children(model, NULL)) == 0)
1002                 return;
1003
1004         last_row = children-1;
1005         if(!gtk_tree_model_iter_nth_child(model, &iter, NULL, last_row))
1006                 return;
1007
1008         path = gtk_tree_model_get_path(model, &iter);
1009
1010         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(packetlist->view),
1011                         path,
1012                         NULL,
1013                         TRUE,   /* use_align */
1014                         0.5f,   /* row_align determines where the row is placed, 0.5 means center */
1015                         0);     /* The horizontal alignment of the column */
1016
1017         gtk_tree_path_free(path);
1018
1019 }
1020
1021 gboolean
1022 packet_list_check_end(void)
1023 {
1024         gboolean at_end = FALSE;
1025         GtkAdjustment *adj;
1026
1027 #if GTK_CHECK_VERSION(3,0,0)
1028         adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (packetlist->view));
1029 #else
1030         adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(packetlist->view));
1031 #endif
1032         g_return_val_if_fail(adj != NULL, FALSE);
1033
1034         if (gtk_adjustment_get_value(adj) >= gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj)) {
1035                 at_end = TRUE;
1036         }
1037 #ifdef HAVE_LIBPCAP
1038         if (gtk_adjustment_get_value(adj) > 0 && at_end != last_at_end && at_end != auto_scroll_live) {
1039                 main_auto_scroll_live_changed(at_end);
1040         }
1041 #endif
1042         last_at_end = at_end;
1043         return at_end;
1044 }
1045
1046 /*
1047  * Given a frame_data structure, scroll to and select the row in the
1048  * packet list corresponding to that frame.  If there is no such
1049  * row, return FALSE, otherwise return TRUE.
1050  */
1051 gboolean
1052 packet_list_select_row_from_data(frame_data *fdata_needle)
1053 {
1054         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
1055         GtkTreeIter iter;
1056
1057         /* Initializes iter with the first iterator in the tree (the one at the path "0")
1058          * and returns TRUE. Returns FALSE if the tree is empty
1059          */
1060         if(!gtk_tree_model_get_iter_first(model, &iter))
1061                 return FALSE;
1062
1063         do {
1064                 frame_data *fdata;
1065
1066                 fdata = packet_list_get_record(model, &iter);
1067
1068                 if(fdata == fdata_needle) {
1069                         scroll_to_and_select_iter(model, NULL, &iter);
1070
1071                         return TRUE;
1072                 }
1073         } while (gtk_tree_model_iter_next(model, &iter));
1074
1075         return FALSE;
1076 }
1077
1078 void
1079 packet_list_set_selected_row(gint row)
1080 {
1081         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
1082         GtkTreeIter iter;
1083         GtkTreeSelection *selection;
1084         GtkTreePath *path;
1085
1086         path = gtk_tree_path_new_from_indices(row-1, -1);
1087
1088         if (!gtk_tree_model_get_iter(model, &iter, path))
1089                 return;
1090
1091         /* Select the row */
1092         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
1093         gtk_tree_selection_select_iter (selection, &iter);
1094
1095         /* "cursor-changed" signal triggers packet_list_select_cb() */
1096         /*  which will update the middle and bottom panes.              */
1097         gtk_tree_view_set_cursor(GTK_TREE_VIEW(packetlist->view),
1098                         path,
1099                         NULL,
1100                         FALSE); /* start_editing */
1101
1102         gtk_tree_path_free(path);
1103 }
1104
1105 static gint
1106 row_number_from_iter(GtkTreeIter *iter)
1107 {
1108         gint row;
1109         gint *indices;
1110         GtkTreePath *path;
1111         GtkTreeModel *model;
1112
1113         model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
1114         path = gtk_tree_model_get_path(model, iter);
1115         indices = gtk_tree_path_get_indices(path);
1116         g_assert(indices);
1117         /* Indices start from 0, but rows start from 1. Hence +1 */
1118         row = indices[0] + 1;
1119
1120         gtk_tree_path_free(path);
1121
1122         return row;
1123 }
1124
1125 static void
1126 packet_list_select_cb(GtkTreeView *tree_view, gpointer data _U_)
1127 {
1128         GtkTreeSelection *selection;
1129         GtkTreeIter iter;
1130         gint row;
1131
1132         if ((selection = gtk_tree_view_get_selection(tree_view)) == NULL)
1133                 return;
1134
1135         if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
1136                 return;
1137
1138         row = row_number_from_iter(&iter);
1139
1140         /* Check if already selected
1141          */
1142         if (cfile.current_frame && cfile.current_row == row)
1143                 return;
1144
1145         /* Remove the hex display tab pages */
1146         while(gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0))
1147                 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
1148
1149         cf_select_packet(&cfile, row);
1150         /* If searching the tree, set the focus there; otherwise, focus on the packet list */
1151         if (cfile.search_in_progress && cfile.decode_data) {
1152                 gtk_widget_grab_focus(tree_view_gbl);
1153         } else {
1154                 gtk_widget_grab_focus(packetlist->view);
1155         }
1156
1157         /* Add newly selected frame to packet history (breadcrumbs) */
1158         packet_history_add(row);
1159 }
1160
1161 static void
1162 packet_list_double_click_cb(GtkTreeView *treeview, GtkTreePath *path _U_,
1163                                 GtkTreeViewColumn *col _U_, gpointer userdata _U_)
1164 {
1165         new_packet_window(GTK_WIDGET(treeview), FALSE, FALSE);
1166 }
1167
1168 gboolean
1169 packet_list_get_event_row_column(GdkEventButton *event_button,
1170                                      gint *physical_row, gint *row, gint *column)
1171 {
1172         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
1173         GtkTreePath *path;
1174         GtkTreeViewColumn *view_column;
1175
1176         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(packetlist->view),
1177                                           (gint) event_button->x,
1178                                           (gint) event_button->y,
1179                                           &path, &view_column, NULL, NULL)) {
1180                 GtkTreeIter iter;
1181                 GList *cols;
1182                 gint *indices;
1183                 frame_data *fdata;
1184
1185                 /* Fetch indices */
1186                 gtk_tree_model_get_iter(model, &iter, path);
1187                 indices = gtk_tree_path_get_indices(path);
1188                 g_assert(indices);
1189                 /* Indices start from 0. Hence +1 */
1190                 *row = indices[0] + 1;
1191                 gtk_tree_path_free(path);
1192
1193                 /* Fetch physical row */
1194                 fdata = packet_list_get_record(model, &iter);
1195                 *physical_row = fdata->num;
1196
1197                 /* Fetch column */
1198                 /* XXX -doesn't work if columns are re-arranged? */
1199                 cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(packetlist->view));
1200                 *column = g_list_index(cols, (gpointer) view_column);
1201                 g_list_free(cols);
1202
1203                 return TRUE;
1204         }
1205         else
1206                 return FALSE;
1207 }
1208
1209 frame_data *
1210 packet_list_get_row_data(gint row)
1211 {
1212         GtkTreePath *path = gtk_tree_path_new();
1213         GtkTreeIter iter;
1214         frame_data *fdata;
1215
1216         g_assert(row > 0);
1217         gtk_tree_path_append_index(path, row-1);
1218         gtk_tree_model_get_iter(GTK_TREE_MODEL(packetlist), &iter, path);
1219
1220         fdata = packet_list_get_record(GTK_TREE_MODEL(packetlist), &iter);
1221
1222         gtk_tree_path_free(path);
1223
1224         return fdata;
1225 }
1226
1227 static void
1228 show_cell_data_func(GtkTreeViewColumn *col _U_, GtkCellRenderer *renderer,
1229                         GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
1230 {
1231         frame_data *fdata = packet_list_get_record(model, iter);
1232
1233         gboolean color_on;
1234         GdkColor fg_gdk;
1235         GdkColor bg_gdk;
1236
1237         if (fdata->flags.ignored) {
1238                 color_t_to_gdkcolor(&fg_gdk, &prefs.gui_ignored_fg);
1239                 color_t_to_gdkcolor(&bg_gdk, &prefs.gui_ignored_bg);
1240                 color_on = TRUE;
1241         } else if (fdata->flags.marked) {
1242                 color_t_to_gdkcolor(&fg_gdk, &prefs.gui_marked_fg);
1243                 color_t_to_gdkcolor(&bg_gdk, &prefs.gui_marked_bg);
1244                 color_on = TRUE;
1245         } else if (fdata->color_filter) {
1246                 const color_filter_t *color_filter = (const color_filter_t *)fdata->color_filter;
1247
1248                 color_t_to_gdkcolor(&fg_gdk, &color_filter->fg_color);
1249                 color_t_to_gdkcolor(&bg_gdk, &color_filter->bg_color);
1250                 color_on = enable_color;
1251         } else
1252                 color_on = FALSE;
1253
1254         if (color_on) {
1255                 g_object_set(renderer,
1256                          "foreground-gdk", &fg_gdk,
1257                          "foreground-set", TRUE,
1258                          "background-gdk", &bg_gdk,
1259                          "background-set", TRUE,
1260                          NULL);
1261         } else {
1262                 g_object_set(renderer,
1263                          "foreground-set", FALSE,
1264                          "background-set", FALSE,
1265                          NULL);
1266         }
1267 }
1268
1269 void
1270 packet_list_enable_color(gboolean enable)
1271 {
1272         enable_color = enable;
1273         gtk_widget_queue_draw (packetlist->view);
1274 }
1275
1276 /* Redraw the packet list *and* currently-selected detail */
1277 void
1278 packet_list_queue_draw(void)
1279 {
1280         GtkTreeSelection *selection;
1281         GtkTreeIter iter;
1282         gint row;
1283
1284         gtk_widget_queue_draw (packetlist->view);
1285
1286         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
1287         if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
1288                 return;
1289         row = row_number_from_iter(&iter);
1290         cf_select_packet(&cfile, row);
1291 }
1292
1293 void
1294 packet_list_set_font(PangoFontDescription *font)
1295 {
1296 #if GTK_CHECK_VERSION(3,0,0)
1297         gtk_widget_override_font(packetlist->view, font);
1298 #else
1299         gtk_widget_modify_font(packetlist->view, font);
1300 #endif
1301 }
1302
1303
1304 /* call this after last set_frame_mark is done */
1305 static void
1306 mark_frames_ready(void)
1307 {
1308         packets_bar_update();
1309         packet_list_queue_draw();
1310 }
1311
1312 static void
1313 set_frame_mark(gboolean set, frame_data *fdata)
1314 {
1315         if (set)
1316                 cf_mark_frame(&cfile, fdata);
1317         else
1318                 cf_unmark_frame(&cfile, fdata);
1319 }
1320
1321 void
1322 packet_list_mark_frame_cb(GtkWidget *w _U_, gpointer data _U_)
1323 {
1324         GtkTreeModel *model;
1325         GtkTreeSelection *selection;
1326         GtkTreeIter iter;
1327         frame_data *fdata;
1328
1329         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
1330         /* model is filled with the current model as a convenience. */
1331         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
1332                 return;
1333
1334         fdata = packet_list_get_record(model, &iter);
1335
1336         set_frame_mark(!fdata->flags.marked, fdata);
1337         mark_frames_ready();
1338 }
1339
1340 static void
1341 mark_all_displayed_frames(gboolean set)
1342 {
1343         /* XXX: we might need a progressbar here */
1344         guint32 framenum;
1345         frame_data *fdata;
1346         for (framenum = 1; framenum <= cfile.count; framenum++) {
1347                 fdata = frame_data_sequence_find(cfile.frame_set_info.frames, framenum);
1348                 if( fdata->flags.passed_dfilter )
1349                         set_frame_mark(set, fdata);
1350         }
1351 }
1352
1353 void
1354 packet_list_mark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_)
1355 {
1356         mark_all_displayed_frames(TRUE);
1357         mark_frames_ready();
1358 }
1359
1360 void
1361 packet_list_unmark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_)
1362 {
1363         mark_all_displayed_frames(FALSE);
1364         mark_frames_ready();
1365 }
1366
1367 static void
1368 toggle_mark_all_displayed_frames(void)
1369 {
1370         /* XXX: we might need a progressbar here */
1371         guint32 framenum;
1372         frame_data *fdata;
1373         for (framenum = 1; framenum <= cfile.count; framenum++) {
1374                 fdata = frame_data_sequence_find(cfile.frame_set_info.frames, framenum);
1375                 if( fdata->flags.passed_dfilter )
1376                         set_frame_mark(!fdata->flags.marked, fdata);
1377         }
1378 }
1379
1380 void
1381 packet_list_toggle_mark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_)
1382 {
1383         toggle_mark_all_displayed_frames();
1384         mark_frames_ready();
1385 }
1386
1387
1388 static void
1389 set_frame_ignore(gboolean set, frame_data *fdata)
1390 {
1391         if (set)
1392                 cf_ignore_frame(&cfile, fdata);
1393         else
1394                 cf_unignore_frame(&cfile, fdata);
1395 }
1396
1397 void
1398 packet_list_ignore_frame_cb(GtkWidget *w _U_, gpointer data _U_)
1399 {
1400         GtkTreeModel *model;
1401         GtkTreeSelection *selection;
1402         GtkTreeIter iter;
1403         frame_data *fdata;
1404
1405         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
1406         /* model is filled with the current model as a convenience. */
1407         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
1408                 return;
1409
1410         fdata = packet_list_get_record(model, &iter);
1411         set_frame_ignore(!fdata->flags.ignored, fdata);
1412         redissect_packets();
1413 }
1414
1415 static void
1416 ignore_all_displayed_frames(gboolean set)
1417 {
1418         guint32 framenum;
1419         frame_data *fdata;
1420
1421         /* XXX: we might need a progressbar here */
1422         for (framenum = 1; framenum <= cfile.count; framenum++) {
1423                 fdata = frame_data_sequence_find(cfile.frame_set_info.frames, framenum);
1424                 if( fdata->flags.passed_dfilter )
1425                         set_frame_ignore(set, fdata);
1426         }
1427         redissect_packets();
1428 }
1429
1430 void
1431 packet_list_ignore_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_)
1432 {
1433         if(cfile.displayed_count < cfile.count){
1434                 frame_data *fdata;
1435                 /* Due to performance impact with large captures, don't check the filtered list for
1436                 an ignored frame; just check the first. If a ignored frame exists but isn't first and
1437                 the user wants to unignore all the displayed frames, they will just re-exec the shortcut. */
1438                 fdata = frame_data_sequence_find(cfile.frame_set_info.frames, cfile.first_displayed);
1439                 if (fdata->flags.ignored==TRUE) {
1440                         ignore_all_displayed_frames(FALSE);
1441                 } else {
1442                         ignore_all_displayed_frames(TRUE);
1443                 }
1444         }
1445 }
1446
1447 static void
1448 unignore_all_frames(void)
1449 {
1450         guint32 framenum;
1451         frame_data *fdata;
1452
1453         /* XXX: we might need a progressbar here */
1454         for (framenum = 1; framenum <= cfile.count; framenum++) {
1455                 fdata = frame_data_sequence_find(cfile.frame_set_info.frames, framenum);
1456                 set_frame_ignore(FALSE, fdata);
1457         }
1458         redissect_packets();
1459 }
1460
1461 void
1462 packet_list_unignore_all_frames_cb(GtkWidget *w _U_, gpointer data _U_)
1463 {
1464         unignore_all_frames();
1465 }
1466
1467
1468 static void
1469 untime_reference_all_frames(void)
1470 {
1471         /* XXX: we might need a progressbar here */
1472         guint32 framenum;
1473         frame_data *fdata;
1474         for (framenum = 1; framenum <= cfile.count && cfile.ref_time_count > 0; framenum++) {
1475                 fdata = frame_data_sequence_find(cfile.frame_set_info.frames, framenum);
1476                 if (fdata->flags.ref_time == 1) {
1477                         set_frame_reftime(FALSE, fdata, cfile.current_row);
1478                 }
1479         }
1480 }
1481
1482 void
1483 packet_list_untime_reference_all_frames_cb(GtkWidget *w _U_, gpointer data _U_)
1484 {
1485         untime_reference_all_frames();
1486 }
1487
1488
1489 guint
1490 packet_list_get_column_id (gint col_num)
1491 {
1492         GtkTreeViewColumn *column = gtk_tree_view_get_column (GTK_TREE_VIEW(packetlist->view), col_num);
1493         gint col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), E_MPACKET_LIST_COL_KEY));
1494
1495         return col_id;
1496 }
1497
1498 void
1499 packet_list_copy_summary_cb(gpointer data _U_, copy_summary_type copy_type)
1500 {
1501         gint col;
1502         gchar *celltext;
1503         GString* text;
1504         GtkTreeModel *model;
1505         GtkTreeSelection *selection;
1506         GtkTreeIter iter;
1507         gboolean first_col = TRUE;
1508
1509         if(CS_CSV == copy_type) {
1510                 text = g_string_new("\"");
1511         } else {
1512                 text = g_string_new("");
1513         }
1514
1515         if (cfile.current_frame) {
1516                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
1517                 /* model is filled with the current model as a convenience.  */
1518                 if (!gtk_tree_selection_get_selected(selection, &model, &iter))
1519                         return;
1520
1521                 for(col = 0; col < cfile.cinfo.num_cols; ++col) {
1522                         if (get_column_visible(col)) {
1523                                 if(!first_col) {
1524                                         if(CS_CSV == copy_type) {
1525                                                 g_string_append(text,"\",\"");
1526                                         } else {
1527                                                 g_string_append_c(text, '\t');
1528                                         }
1529                                 }
1530
1531                                 gtk_tree_model_get(model, &iter, packet_list_get_column_id(col), &celltext, -1);
1532                                 g_string_append(text,celltext);
1533                                 g_free(celltext);
1534                                 first_col = FALSE;
1535                         }
1536                 }
1537                 if(CS_CSV == copy_type) {
1538                         g_string_append_c(text,'"');
1539                 }
1540                 copy_to_clipboard(text);
1541         }
1542         g_string_free(text,TRUE);
1543 }
1544
1545 gchar *
1546 packet_list_get_packet_comment(void)
1547 {
1548         GtkTreeModel *model;
1549         GtkTreeSelection *selection;
1550         GtkTreeIter iter;
1551         frame_data *fdata;
1552
1553         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
1554         /* model is filled with the current model as a convenience. */
1555         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
1556                 return NULL;
1557
1558         fdata = packet_list_get_record(model, &iter);
1559
1560         return cf_get_packet_comment(&cfile, fdata);
1561 }
1562
1563 void
1564 packet_list_return_all_comments(GtkTextBuffer *buffer)
1565 {
1566         guint32 framenum;
1567         frame_data *fdata;
1568         gchar *buf_str;
1569
1570         for (framenum = 1; framenum <= cfile.count ; framenum++) {
1571                 char *pkt_comment;
1572
1573                 fdata = frame_data_sequence_find(cfile.frame_set_info.frames, framenum);
1574                 pkt_comment = cf_get_packet_comment(&cfile, fdata);
1575                 if (pkt_comment) {
1576                         buf_str = g_strdup_printf("Frame %u: %s \n\n",framenum, pkt_comment);
1577                         gtk_text_buffer_insert_at_cursor (buffer, buf_str, -1);
1578                         g_free(buf_str);
1579                         g_free(pkt_comment);
1580                 }
1581                 if (gtk_text_buffer_get_char_count(buffer) > MAX_COMMENTS_TO_FETCH) {
1582                         buf_str = g_strdup_printf("[ Comment text exceeds %s. Stopping. ]",
1583                                                   format_size(MAX_COMMENTS_TO_FETCH, (format_size_flags_e)(format_size_unit_bytes|format_size_prefix_si)));
1584                         gtk_text_buffer_insert_at_cursor (buffer, buf_str, -1);
1585                         return;
1586                 }
1587         }
1588 }
1589
1590 void
1591 packet_list_update_packet_comment(gchar *new_packet_comment)
1592 {
1593
1594         GtkTreeModel *model;
1595         GtkTreeSelection *selection;
1596         GtkTreeIter iter;
1597         frame_data *fdata;
1598
1599         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
1600         /* model is filled with the current model as a convenience. */
1601         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
1602                 return;
1603
1604         fdata = packet_list_get_record(model, &iter);
1605
1606         /* Check if we are clearing the comment */
1607         if(strlen(new_packet_comment) == 0) {
1608                 g_free(new_packet_comment);
1609                 new_packet_comment = NULL;
1610         }
1611
1612         cf_set_user_packet_comment(&cfile, fdata, new_packet_comment);
1613
1614         g_free(new_packet_comment);
1615
1616         /* Update the main window, as we now have unsaved changes. */
1617         main_update_for_unsaved_changes(&cfile);
1618
1619         packet_list_queue_draw();
1620
1621 }
1622
1623 void
1624 packet_list_recent_write_all(FILE *rf)
1625 {
1626         gint col, width, num_cols, col_fmt;
1627         GtkTreeViewColumn *tree_column;
1628         gchar xalign;
1629
1630         fprintf (rf, "%s:", RECENT_KEY_COL_WIDTH);
1631         num_cols = g_list_length(prefs.col_list);
1632         for (col = 0; col < num_cols; col++) {
1633                 col_fmt = get_column_format(col);
1634                 if (col_fmt == COL_CUSTOM) {
1635                         fprintf (rf, " \"%%Cus:%s\",", get_column_custom_fields(col));
1636                 } else {
1637                         fprintf (rf, " %s,", col_format_to_string(col_fmt));
1638                 }
1639                 tree_column = gtk_tree_view_get_column(GTK_TREE_VIEW(packetlist->view), col);
1640                 width = gtk_tree_view_column_get_width(tree_column);
1641                 xalign = recent_get_column_xalign (col);
1642                 if (width == 0) {
1643                         /* We have not initialized the packet list yet, use old values */
1644                         width = recent_get_column_width (col);
1645                 }
1646                 fprintf (rf, " %d", width);
1647                 if (xalign != COLUMN_XALIGN_DEFAULT) {
1648                         fprintf (rf, ":%c", xalign);
1649                 }
1650                 if (col != num_cols-1) {
1651                         fprintf (rf, ",");
1652                 }
1653         }
1654         fprintf (rf, "\n");
1655 }
1656
1657 GtkWidget *
1658 packet_list_get_widget(void)
1659 {
1660         g_assert(packetlist);
1661         g_assert(packetlist->view);
1662         return packetlist->view;
1663 }
1664
1665 void
1666 packet_list_colorize_packets(void)
1667 {
1668         packet_list_reset_colorized(packetlist);
1669         gtk_widget_queue_draw (packetlist->view);
1670 }
1671
1672 static gboolean
1673 query_packet_list_tooltip_cb(GtkWidget *widget, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip, gpointer data _U_)
1674 {
1675         GtkTreeIter iter;
1676         GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
1677         GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
1678         GtkTreePath *path = NULL;
1679         GtkTreeViewColumn *column;
1680         gint col, num_cols;
1681         frame_data *fdata;
1682         GtkCellRenderer* renderer=NULL;
1683         GList *renderer_list;
1684         gboolean result = FALSE;
1685
1686         if (!gtk_tree_view_get_tooltip_context(tree_view, &x, &y, keyboard_tip, &model, &path, &iter))
1687                 return result;
1688
1689         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tree_view), x, y, NULL, &column, NULL, NULL)) {
1690                 char *pkt_comment;
1691
1692                 num_cols = g_list_length(prefs.col_list);
1693
1694                 for (col = 0; col < num_cols; col++) {
1695                         if (gtk_tree_view_get_column(tree_view, col) == column)
1696                                 break;
1697                 }
1698
1699                 fdata = packet_list_get_record(model, &iter);
1700                 pkt_comment = cf_get_packet_comment(&cfile, fdata);
1701
1702                 if (pkt_comment != NULL) {
1703                         gtk_tooltip_set_markup(tooltip, pkt_comment);
1704                         renderer_list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1705                         /* get the first renderer */
1706                         if (g_list_first(renderer_list)) {
1707                                 renderer = (GtkCellRenderer*)g_list_nth_data(renderer_list, 0);
1708                                 gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, column, renderer);
1709                         }
1710                         g_list_free(renderer_list);
1711                         g_free(pkt_comment);
1712                         result = TRUE;
1713                 }
1714         }
1715         gtk_tree_path_free(path);
1716
1717         return result;
1718 }
1719
1720 /*
1721  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1722  *
1723  * Local variables:
1724  * c-basic-offset: 8
1725  * tab-width: 8
1726  * indent-tabs-mode: t
1727  * End:
1728  *
1729  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1730  * :indentSize=8:tabSize=8:noTabs=false:
1731  */