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