2 * Routines to implement a custom GTK+ list model for Wireshark's packet list
3 * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
4 * * Co-authors Anders Broman and Kovarththanan Rajaratnam.
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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,
26 /* This code was originally based on the GTK+ Tree View tutorial at
27 * http://scentric.net/tutorial */
36 #include "packet_list_store.h"
38 #include "ui/progress_dlg.h"
39 #include "ui/ws_ui_util.h"
41 #include "ui/gtk/old-gtk-compat.h"
43 #include <epan/epan_dissect.h>
44 #include <epan/column.h>
46 #include <epan/color_filters.h>
47 #include "frame_tvbuff.h"
51 /* #define PACKET_PARANOID_CHECKS */
53 /** PacketListRecord: represents a row */
54 typedef struct _PacketListRecord {
55 /** The column text for some columns */
56 const gchar **col_text;
57 /**< The length of the column text strings in 'col_text' */
58 gushort *col_text_len;
62 /* admin stuff used by the custom list model */
63 #ifdef PACKET_PARANOID_CHECKS
64 /** position within the physical array */
67 /** position within the visible array */
70 /** Has this record been colorized? */
75 static void packet_list_init(PacketList *pkg_tree);
76 static void packet_list_class_init(PacketListClass *klass);
77 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
78 static void packet_list_finalize(GObject *object);
79 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
80 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
81 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint idx);
82 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
83 GtkTreeIter *iter, GtkTreePath *path);
84 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
86 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
87 gint column, GValue *value);
88 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
90 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
93 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
94 GtkTreeIter *iter _U_);
95 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
97 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
101 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
102 GtkTreeIter *iter _U_,
103 GtkTreeIter *child _U_);
105 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
109 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
112 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
114 GtkTreeIterCompareFunc sort_func,
116 GDestroyNotify destroy_func);
117 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
119 GtkTreeIterCompareFunc
124 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
126 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
127 static void packet_list_resort(PacketList *packet_list);
128 static void packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color );
130 static GObjectClass *parent_class = NULL;
134 packet_list_get_type(void)
136 static GType packet_list_type = 0;
138 if(packet_list_type == 0) {
139 static const GTypeInfo packet_list_info = {
140 sizeof(PacketListClass),
141 NULL, /* base_init */
142 NULL, /* base_finalize */
143 (GClassInitFunc) packet_list_class_init,
144 NULL, /* class finalize */
145 NULL, /* class_data */
148 (GInstanceInitFunc) packet_list_init,
149 NULL /* value_table */
152 static const GInterfaceInfo tree_model_info = {
153 (GInterfaceInitFunc) packet_list_tree_model_init,
158 static const GInterfaceInfo tree_sortable_info = {
159 (GInterfaceInitFunc) packet_list_sortable_init,
164 /* Register the new derived type with the GObject type system */
165 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
170 g_type_add_interface_static(packet_list_type,
175 /* Register our GtkTreeModel interface with the type system */
176 g_type_add_interface_static(packet_list_type,
177 GTK_TYPE_TREE_SORTABLE,
178 &tree_sortable_info);
181 return packet_list_type;
185 packet_list_sortable_init(GtkTreeSortableIface *iface)
187 iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
188 iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
189 /* The following three functions are not implemented */
190 iface->set_sort_func = packet_list_sortable_set_sort_func;
191 iface->set_default_sort_func =
192 packet_list_sortable_set_default_sort_func;
193 iface->has_default_sort_func =
194 packet_list_sortable_has_default_sort_func;
198 packet_list_class_init(PacketListClass *klass)
200 GObjectClass *object_class;
202 parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
203 object_class = (GObjectClass*) klass;
205 object_class->finalize = packet_list_finalize;
207 #if !GTK_CHECK_VERSION(3,0,0)
208 /* XXX this seems to affect TreeView Application wide
209 * Move to main.c ??? as it's not a bad thing(tm)
211 gtk_rc_parse_string (
212 "style \"PacketList-style\"\n"
214 " GtkTreeView::horizontal-separator = 0\n"
215 " GtkTreeView::vertical-separator = 1\n"
216 "} widget_class \"*TreeView*\""
217 " style \"PacketList-style\"");
223 packet_list_tree_model_init(GtkTreeModelIface *iface)
225 iface->get_flags = packet_list_get_flags;
226 iface->get_n_columns = packet_list_get_n_columns;
227 iface->get_column_type = packet_list_get_column_type;
228 iface->get_iter = packet_list_get_iter;
229 iface->get_path = packet_list_get_path;
230 iface->get_value = packet_list_get_value;
231 iface->iter_next = packet_list_iter_next;
232 iface->iter_children = packet_list_iter_children;
233 iface->iter_has_child = packet_list_iter_has_child;
234 iface->iter_n_children = packet_list_iter_n_children;
235 iface->iter_nth_child = packet_list_iter_nth_child;
236 iface->iter_parent = packet_list_iter_parent;
239 /* This is called every time a new packet list object instance is created in
240 * packet_list_new. Initialize the list structure's fields here. */
242 packet_list_init(PacketList *packet_list)
246 /* To check whether an iter belongs to our model. */
247 packet_list->stamp = g_random_int();
249 packet_list->n_cols = cfile.cinfo.num_cols;
250 packet_list->physical_rows = g_ptr_array_new();
251 packet_list->visible_rows = g_ptr_array_new();
253 packet_list->columnized = FALSE;
254 packet_list->sort_id = 0; /* defaults to first column for now */
255 packet_list->sort_order = GTK_SORT_ASCENDING;
257 packet_list->col_to_text = g_new(int, packet_list->n_cols);
258 for (i = 0, j = 0; i < packet_list->n_cols; i++) {
259 if (!col_based_on_frame_data(&cfile.cinfo, i)) {
260 packet_list->col_to_text[i] = j;
263 packet_list->col_to_text[i] = -1;
265 packet_list->n_text_cols = j;
267 #ifdef PACKET_LIST_STATISTICS
268 packet_list->const_strings = 0;
272 /* This function is called just before a packet list is destroyed. Free
273 * dynamically allocated memory here. */
275 packet_list_finalize(GObject *object)
277 /* PacketList *packet_list = PACKET_LIST(object); */
279 /* XXX - Free all records and free all memory used by the list */
281 /* must chain up - finalize parent */
282 (* parent_class->finalize) (object);
285 static GtkTreeModelFlags
286 packet_list_get_flags(GtkTreeModel *tree_model)
288 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
289 (GtkTreeModelFlags)0);
291 return (GtkTreeModelFlags)(GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
295 packet_list_get_n_columns(GtkTreeModel *tree_model)
297 PacketList *packet_list;
299 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
300 packet_list = (PacketList *) tree_model;
302 /* Note: We need one extra column to store the entire frame_data */
303 return packet_list->n_cols + 1;
307 packet_list_get_column_type(GtkTreeModel *tree_model, gint idx)
309 PacketList *packet_list;
311 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
312 packet_list = (PacketList *) tree_model;
314 /* Note: We use one extra column to store the entire frame_data */
315 g_return_val_if_fail(idx >= 0 && idx < packet_list->n_cols + 1, G_TYPE_INVALID);
317 if (idx >= 0 && idx < packet_list->n_cols)
318 return G_TYPE_STRING;
319 else if (idx == packet_list->n_cols)
320 return G_TYPE_POINTER;
322 return G_TYPE_INVALID;
326 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
329 gint *indices, depth;
331 g_assert(PACKETLIST_IS_LIST(tree_model));
333 g_assert(path != NULL);
335 indices = gtk_tree_path_get_indices(path);
336 depth = gtk_tree_path_get_depth(path);
338 /* we do not allow children since it's just a list */
339 g_assert(depth == 1);
341 return packet_list_iter_nth_child(tree_model, iter, NULL, indices[0]);
345 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
348 PacketListRecord *record;
349 PacketList *packet_list;
351 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
352 packet_list = (PacketList *) tree_model;
354 g_return_val_if_fail(iter != NULL, NULL);
355 g_return_val_if_fail(iter->stamp == packet_list->stamp, NULL);
356 g_return_val_if_fail(iter->user_data != NULL, NULL);
358 record = (PacketListRecord*) iter->user_data;
360 path = gtk_tree_path_new();
361 gtk_tree_path_append_index(path, record->visible_pos);
367 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
370 PacketListRecord *record;
371 PacketList *packet_list;
373 g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
374 packet_list = (PacketList *) tree_model;
376 g_return_if_fail(iter != NULL);
377 g_return_if_fail(iter->stamp == packet_list->stamp);
378 g_return_if_fail(iter->user_data != NULL);
380 /* Note: We use one extra column to store the entire frame_data */
381 g_return_if_fail(column >= 0 && column < packet_list->n_cols + 1);
383 record = (PacketListRecord*) iter->user_data;
385 #ifdef PACKET_PARANOID_CHECKS
386 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->physical_rows, record->physical_pos));
388 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, record->visible_pos));
390 if (column >= 0 && column < packet_list->n_cols) {
393 g_value_init(value, G_TYPE_STRING);
395 if (record->col_text == NULL || !record->colorized)
396 packet_list_dissect_and_cache_record(packet_list, record, !record->colorized);
398 text_column = packet_list->col_to_text[column];
399 if (text_column == -1) { /* column based on frame_data */
400 col_fill_in_frame_data(record->fdata, &cfile.cinfo, column, FALSE);
401 g_value_set_string(value, cfile.cinfo.columns[column].col_data);
403 g_return_if_fail(record->col_text);
404 g_value_set_string(value, record->col_text[text_column]);
407 } else if (column == packet_list->n_cols) {
408 g_value_init(value, G_TYPE_POINTER);
409 g_value_set_pointer(value, record->fdata);
413 static PacketListRecord *
414 packet_list_iter_next_visible(PacketList *packet_list, PacketListRecord *record)
416 PacketListRecord *nextrecord;
417 gint next_visible_pos;
419 g_assert(record->visible_pos >= 0);
420 next_visible_pos = record->visible_pos + 1;
422 /* Is this the last record in the list? */
423 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, next_visible_pos))
426 nextrecord = PACKET_LIST_RECORD_GET(packet_list->visible_rows, next_visible_pos);
428 g_assert(nextrecord->visible_pos == (record->visible_pos + 1));
429 #ifdef PACKET_PARANOID_CHECKS
430 g_assert(nextrecord->physical_pos >= (record->physical_pos + 1));
436 /* Takes an iter structure and sets it to point to the next row. */
438 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
440 PacketListRecord *record, *nextrecord;
441 PacketList *packet_list;
443 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
444 packet_list = (PacketList *) tree_model;
449 g_return_val_if_fail(iter->stamp == packet_list->stamp, FALSE);
450 g_return_val_if_fail(iter->user_data, FALSE);
452 record = (PacketListRecord*) iter->user_data;
453 nextrecord = packet_list_iter_next_visible(packet_list, record);
458 /* iter->stamp = packet_list->stamp; */
459 iter->user_data = nextrecord;
465 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
468 return packet_list_iter_nth_child(tree_model, iter, parent, 0);
472 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
474 return FALSE; /* Lists have no children */
478 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
480 PacketList *packet_list;
482 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
483 packet_list = (PacketList *) tree_model;
486 /* special case: if iter == NULL, return number of top-level rows */
487 return PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
490 g_return_val_if_fail(iter->stamp == packet_list->stamp, 0);
491 g_return_val_if_fail(iter->user_data, 0);
492 /* Lists have zero children */
498 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
499 GtkTreeIter *parent, gint n)
501 PacketListRecord *record;
502 PacketList *packet_list;
504 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
505 packet_list = (PacketList *) tree_model;
507 /* A list only has top-level rows */
509 g_return_val_if_fail(parent->stamp == packet_list->stamp, FALSE);
510 g_return_val_if_fail(parent->user_data, FALSE);
514 /* Special case: if parent == NULL, set iter to n-th top-level row. */
515 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
518 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
520 g_assert(record->visible_pos == n);
522 iter->stamp = packet_list->stamp;
523 iter->user_data = record;
529 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
530 GtkTreeIter *child _U_)
532 return FALSE; /* No parents since no children in a list */
536 packet_list_new(void)
538 PacketList *newpacketlist;
540 newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
542 g_assert(newpacketlist != NULL);
544 return newpacketlist;
549 packet_list_row_deleted(PacketList *packet_list, guint pos)
553 /* Inform the tree view and other interested objects (such as tree row
554 * references) that we have deleted a row */
555 path = gtk_tree_path_new();
556 gtk_tree_path_append_index(path, pos);
558 gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
560 gtk_tree_path_free(path);
565 packet_list_store_clear(PacketList *packet_list)
567 g_return_if_fail(packet_list != NULL);
568 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
570 /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
571 * the model from the view.
572 for( ; packet_list->num_rows > 0; --packet_list->num_rows)
573 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
576 /* XXX - hold on to these rows and reuse them instead */
577 if(packet_list->physical_rows)
578 g_ptr_array_free(packet_list->physical_rows, TRUE);
579 if(packet_list->visible_rows)
580 g_ptr_array_free(packet_list->visible_rows, TRUE);
581 packet_list->physical_rows = g_ptr_array_new();
582 packet_list->visible_rows = g_ptr_array_new();
584 packet_list->columnized = FALSE;
586 /* Generate new number */
587 packet_list->stamp = g_random_int();
589 if (packet_list->string_pool) {
590 g_string_chunk_clear(packet_list->string_pool);
593 #ifdef PACKET_LIST_STATISTICS
594 g_warning("Const strings: %u", packet_list->const_strings);
595 packet_list->const_strings = 0;
600 packet_list_append_record(PacketList *packet_list, frame_data *fdata)
602 PacketListRecord *newrecord;
604 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), -1);
606 newrecord = wmem_new(wmem_file_scope(), PacketListRecord);
607 newrecord->colorized = FALSE;
608 newrecord->col_text_len = NULL;
609 newrecord->col_text = NULL;
610 newrecord->fdata = fdata;
611 #ifdef PACKET_PARANOID_CHECKS
612 newrecord->physical_pos = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
615 if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
616 newrecord->visible_pos = PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
617 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, newrecord);
620 newrecord->visible_pos = -1;
622 PACKET_LIST_RECORD_APPEND(packet_list->physical_rows, newrecord);
624 packet_list->columnized = FALSE; /* XXX, dissect? */
627 * Issue a row_inserted signal if the model is connected
628 * and the row is visible.
630 if (gtk_tree_view_get_model(GTK_TREE_VIEW(packet_list->view)) && newrecord->visible_pos != -1) {
634 path = gtk_tree_path_new();
635 gtk_tree_path_append_index(path, newrecord->visible_pos);
637 iter.stamp = packet_list->stamp;
638 iter.user_data = newrecord;
640 gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
641 gtk_tree_path_free(path);
644 /* XXXX If the model is connected and sort column != frame_num we should
646 * Don't resort the list for every row, the list will be in packet order any way.
647 * packet_list_resort(packet_list);
650 return newrecord->visible_pos;
653 #define PACKET_STRING_CHUNK_SIZE (1 * 1024 * 1024)
655 packet_list_change_record(PacketList *packet_list, PacketListRecord *record, gint col, column_info *cinfo)
660 col_item_t* col_item;
662 text_col = packet_list->col_to_text[col];
664 /* Column based on frame_data or it already contains a value */
665 if (text_col == -1 || record->col_text[text_col] != NULL)
668 col_item = &cfile.cinfo.columns[col];
669 switch (col_item->col_fmt) {
674 case COL_8021Q_VLAN_ID:
677 if (col_item->col_data && col_item->col_data != col_item->col_buf) {
678 col_text_len = strlen(col_item->col_data);
679 if (col_text_len > G_MAXUSHORT)
680 col_text_len = G_MAXUSHORT;
682 /* This is a constant string, so we don't have to copy it */
683 record->col_text[text_col] = (gchar *) col_item->col_data;
684 record->col_text_len[text_col] = (gushort) col_text_len;
685 #ifdef PACKET_LIST_STATISTICS
686 ++packet_list->const_strings;
690 /* !! FALL-THROUGH!! */
693 case COL_RES_SRC: /* COL_DEF_SRC is currently just like COL_RES_SRC */
697 case COL_UNRES_DL_SRC:
698 case COL_DEF_NET_SRC:
699 case COL_RES_NET_SRC:
700 case COL_UNRES_NET_SRC:
702 case COL_RES_DST: /* COL_DEF_DST is currently just like COL_RES_DST */
706 case COL_UNRES_DL_DST:
707 case COL_DEF_NET_DST:
708 case COL_RES_NET_DST:
709 case COL_UNRES_NET_DST:
711 if(col_item->col_data){
712 col_text_len = strlen(col_item->col_data);
713 if (col_text_len > G_MAXUSHORT)
714 col_text_len = G_MAXUSHORT;
716 record->col_text_len[text_col] = (gushort) col_text_len;
718 if (!record->col_text_len[text_col]) {
719 record->col_text[text_col] = "";
720 #ifdef PACKET_LIST_STATISTICS
721 ++packet_list->const_strings;
726 if(!packet_list->string_pool)
727 packet_list->string_pool = g_string_chunk_new(PACKET_STRING_CHUNK_SIZE);
728 if (!get_column_resolved (col) && cinfo->col_expr.col_expr_val[col]) {
729 /* Use the unresolved value in col_expr_val */
730 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_expr.col_expr_val[col]);
732 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)col_item->col_data);
734 record->col_text[text_col] = str;
740 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
744 PacketList *packet_list;
746 g_return_val_if_fail(sortable != NULL, FALSE);
747 g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
749 packet_list = (PacketList *) sortable;
752 *sort_col_id = packet_list->sort_id;
755 *order = packet_list->sort_order;
761 packet_list_column_contains_values(PacketList *packet_list, gint sort_col_id)
763 if (packet_list->columnized || col_based_on_frame_data(&cfile.cinfo, sort_col_id))
769 /* packet_list_dissect_and_cache_all()
771 * TRUE if columnization completed;
772 * packet_list->columnized set to TRUE;
773 * FALSE: columnization did not complete (i.e., was stopped by the user);
774 * packet_list->columnized unchanged (i.e., FALSE).
778 packet_list_dissect_and_cache_all(PacketList *packet_list)
780 PacketListRecord *record;
782 int progbar_nextstep;
784 gboolean progbar_stop_flag;
785 GTimeVal progbar_start_time;
787 progdlg_t *progbar = NULL;
788 gchar progbar_status_str[100];
789 gint progbar_loop_max;
790 gint progbar_loop_var;
791 gint progbar_updates = 100 /* 100% */;
793 g_assert(packet_list->columnized == FALSE);
795 progbar_loop_max = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
796 /* Update the progress bar when it gets to this value. */
797 progbar_nextstep = 0;
798 /* When we reach the value that triggers a progress bar update,
799 bump that value by this amount. */
800 progbar_quantum = progbar_loop_max/progbar_updates;
801 /* Progress so far. */
804 progbar_stop_flag = FALSE;
805 g_get_current_time(&progbar_start_time);
807 main_window_update();
809 for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var) {
810 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, progbar_loop_var);
811 packet_list_dissect_and_cache_record(packet_list, record, FALSE);
813 /* Create the progress bar if necessary.
814 We check on every iteration of the loop, so that it takes no
815 longer than the standard time to create it (otherwise, for a
816 large file, we might take considerably longer than that standard
817 time in order to get to the next progress bar step). */
819 /* Note: The following may call gtk_main_iteration() which will */
820 /* allow certain "interupts" to happen during this code. */
821 /* (Note that the progress_dlg window is set to "modal" */
822 /* so that clicking on other windows is disabled). */
823 progbar = delayed_create_progress_dlg(gtk_widget_get_window(packet_list->view),
824 "Construct", "Columns",
825 TRUE, &progbar_stop_flag,
826 &progbar_start_time, progbar_val);
828 if (progbar_loop_var >= progbar_nextstep) {
829 /* let's not divide by zero. We should never be started
830 * with count == 0, so let's assert that */
831 g_assert(progbar_loop_max > 0);
833 progbar_val = (gfloat) progbar_loop_var / progbar_loop_max;
835 if (progbar != NULL) {
836 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
837 "%u of %u frames", progbar_loop_var+1, progbar_loop_max);
838 /* Note: See comment above re use of gtk_main_iteration() */
839 update_progress_dlg(progbar, progbar_val, progbar_status_str);
842 progbar_nextstep += progbar_quantum;
845 if (progbar_stop_flag) {
846 /* Well, the user decided to abort ... */
851 /* We're done; destroy the progress bar if it was created. */
853 destroy_progress_dlg(progbar);
855 if (progbar_stop_flag) {
856 return FALSE; /* user aborted before columnization completed */
859 packet_list->columnized = TRUE;
863 /* packet_list_do_packet_list_dissect_and_cache_all()
865 * TRUE: if columnization not needed or columnization completed;
866 * FALSE: columnization did not complete (i.e., stopped by the user)
869 packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id)
871 if (!packet_list_column_contains_values(packet_list, sort_col_id)) {
872 return packet_list_dissect_and_cache_all(packet_list);
878 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
882 PacketList *packet_list;
884 g_return_if_fail(sortable != NULL);
885 g_return_if_fail(PACKETLIST_IS_LIST(sortable));
887 packet_list = (PacketList *) sortable;
889 if(packet_list->sort_id == sort_col_id &&
890 packet_list->sort_order == order)
893 packet_list->sort_id = sort_col_id;
894 packet_list->sort_order = order;
896 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
899 packet_list_resort(packet_list);
901 /* emit "sort-column-changed" signal to tell any tree views
902 * that the sort column has changed (so the little arrow
903 * in the column header of the sort column is drawn
904 * in the right column) */
906 gtk_tree_sortable_sort_column_changed(sortable);
910 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
911 gint sort_col_id _U_,
912 GtkTreeIterCompareFunc sort_func _U_,
913 gpointer user_data _U_,
914 GDestroyNotify destroy_func _U_)
916 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
920 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
921 GtkTreeIterCompareFunc sort_func _U_,
922 gpointer user_data _U_,
923 GDestroyNotify destroy_func _U_)
925 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
929 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
931 return FALSE; /* Since packet_list_sortable_set_sort_func and
932 set_default_sort_func are not implemented. */
936 packet_list_compare_custom(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
938 header_field_info *hfi;
940 hfi = proto_registrar_get_byname(cfile.cinfo.columns[sort_id].col_custom_fields);
943 return frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
944 } else if ((hfi->strings == NULL) &&
945 (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
946 ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
947 (hfi->display == BASE_OCT))) ||
948 (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
949 (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
950 (hfi->type == FT_RELATIVE_TIME)))
952 /* Attempt to convert to numbers */
953 double num_a = g_ascii_strtod(a->col_text[text_sort_id], NULL);
954 double num_b = g_ascii_strtod(b->col_text[text_sort_id], NULL);
958 else if (num_a > num_b)
964 return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
968 _packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
970 g_assert(a->col_text);
971 g_assert(b->col_text);
972 g_assert(a->col_text[text_sort_id]);
973 g_assert(b->col_text[text_sort_id]);
975 if(a->col_text[text_sort_id] == b->col_text[text_sort_id])
976 return 0; /* no need to call strcmp() */
978 if (cfile.cinfo.columns[sort_id].col_fmt == COL_CUSTOM)
979 return packet_list_compare_custom(sort_id, text_sort_id, a, b);
981 return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
985 packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
989 if (text_sort_id == -1) /* based on frame_data ? */
990 return frame_data_compare(cfile.epan, a->fdata, b->fdata, cfile.cinfo.columns[sort_id].col_fmt);
992 ret = _packet_list_compare_records(sort_id, text_sort_id, a, b);
994 ret = frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
999 packet_list_qsort_physical_compare_func(PacketListRecord **a, PacketListRecord **b,
1000 PacketList *packet_list)
1003 gint sort_id = packet_list->sort_id;
1005 g_assert((a) && (b) && (packet_list));
1007 ret = packet_list_compare_records(sort_id, packet_list->col_to_text[sort_id], *a, *b);
1009 /* Swap -1 and 1 if sort order is reverse */
1010 if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
1011 ret = (ret < 0) ? 1 : -1;
1017 packet_list_resort(PacketList *packet_list)
1019 PacketListRecord *record;
1025 g_return_if_fail(packet_list != NULL);
1026 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1028 if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1031 /* resort physical rows according to sorting column */
1032 g_ptr_array_sort_with_data(packet_list->physical_rows,
1033 (GCompareDataFunc) packet_list_qsort_physical_compare_func,
1035 g_return_if_fail(packet_list->visible_rows != NULL);
1037 /* let other objects know about the new order */
1038 neworder = g_new0(gint, PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1041 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1042 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1043 #ifdef PACKET_PARANOID_CHECKS
1044 record->physical_pos = phy_idx;
1046 g_assert(record->visible_pos >= -1);
1047 if (record->visible_pos >= 0) {
1048 g_assert(record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
1049 neworder[vis_idx] = record->visible_pos;
1050 PACKET_LIST_RECORD_SET(packet_list->visible_rows, vis_idx, record);
1051 record->visible_pos = vis_idx;
1056 g_assert(vis_idx == PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1058 path = gtk_tree_path_new();
1060 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
1063 gtk_tree_path_free(path);
1068 packet_list_recreate_visible_rows_list(PacketList *packet_list)
1072 PacketListRecord *record;
1074 g_return_val_if_fail(packet_list != NULL, 0);
1075 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), 0);
1077 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
1080 if(packet_list->visible_rows)
1081 g_ptr_array_free(packet_list->visible_rows, TRUE);
1083 packet_list->visible_rows = g_ptr_array_new();
1085 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1086 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1087 if (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time) {
1088 record->visible_pos = vis_idx++;
1089 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, record);
1092 record->visible_pos = -1;
1099 packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color)
1105 gboolean create_proto_tree;
1106 struct wtap_pkthdr phdr; /* Packet header */
1107 Buffer buf; /* Packet data */
1108 gboolean dissect_columns = (record->col_text == NULL);
1110 g_return_if_fail(packet_list);
1111 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1113 wtap_phdr_init(&phdr);
1115 fdata = record->fdata;
1117 if (dissect_columns) {
1118 cinfo = &cfile.cinfo;
1120 record->col_text = (const gchar **)wmem_alloc0(wmem_file_scope(), sizeof(*record->col_text) * packet_list->n_text_cols);
1121 record->col_text_len = (gushort *)wmem_alloc0(wmem_file_scope(), sizeof(*record->col_text_len) * packet_list->n_text_cols);
1125 ws_buffer_init(&buf, 1500);
1126 if (!cf_read_record_r(&cfile, fdata, &phdr, &buf)) {
1128 * Error reading the record.
1130 * Don't set the color filter for now (we might want
1131 * to colorize it in some fashion to warn that the
1132 * row couldn't be filled in or colorized), and
1133 * set the columns to placeholder values, except
1134 * for the Info column, where we'll put in an
1137 if (dissect_columns) {
1138 col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
1140 for(col = 0; col < cinfo->num_cols; ++col)
1141 packet_list_change_record(packet_list, record, col, cinfo);
1143 if (dissect_color) {
1144 fdata->color_filter = NULL;
1145 record->colorized = TRUE;
1147 ws_buffer_free(&buf);
1148 return; /* error reading the record */
1152 * Determine whether we need to create a protocol tree.
1155 * we're going to apply a color filter to this packet;
1157 * we're need to fill in the columns and we have custom columns
1158 * (which require field values, which currently requires that
1159 * we build a protocol tree).
1161 create_proto_tree = (dissect_color && color_filters_used()) ||
1162 (dissect_columns && have_custom_cols(cinfo));
1164 epan_dissect_init(&edt, cfile.epan,
1166 FALSE /* proto_tree_visible */);
1168 if (dissect_color) {
1169 color_filters_prime_edt(&edt);
1170 fdata->flags.need_colorize = 1;
1172 if (dissect_columns)
1173 col_custom_prime_edt(&edt, cinfo);
1176 * XXX - need to catch an OutOfMemoryError exception and
1177 * attempt to recover from it.
1179 epan_dissect_run(&edt, cfile.cd_t, &phdr,
1180 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
1183 if (dissect_columns) {
1184 /* "Stringify" non frame_data vals */
1185 epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */);
1187 for(col = 0; col < cinfo->num_cols; ++col)
1188 packet_list_change_record(packet_list, record, col, cinfo);
1192 record->colorized = TRUE;
1194 epan_dissect_cleanup(&edt);
1195 wtap_phdr_cleanup(&phdr);
1196 ws_buffer_free(&buf);
1200 packet_list_reset_colorized(PacketList *packet_list)
1202 PacketListRecord *record;
1205 for(i = 0; i < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++i) {
1206 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, i);
1207 record->colorized = FALSE;
1212 packet_list_get_widest_column_string(PacketList *packet_list, gint col)
1216 g_return_val_if_fail(packet_list != NULL, NULL);
1217 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), NULL);
1218 /* We need real column here, so not packet_list->n_cols+1 */
1219 g_return_val_if_fail(col >= 0 && col < packet_list->n_cols, NULL);
1221 if (PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1224 text_col = packet_list->col_to_text[col];
1226 if (text_col == -1) { /* column based on frame data */
1227 PacketListRecord *record;
1230 guint widest_packet = 0;
1231 gint widest_column_len = -1;
1233 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1236 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1238 col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1239 column_len = (gint) strlen(cfile.cinfo.columns[col].col_buf);
1240 if (column_len > widest_column_len) {
1241 widest_column_len = column_len;
1242 widest_packet = vis_idx;
1246 if (widest_column_len != -1) {
1247 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, widest_packet);
1248 col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1250 return cfile.cinfo.columns[col].col_buf;
1255 PacketListRecord *record;
1258 const gchar *widest_column_str = NULL;
1259 guint widest_column_len = 0;
1261 if (!packet_list->columnized)
1262 packet_list_dissect_and_cache_all(packet_list); /* XXX: need to handle case of "incomplete" ? */
1264 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1265 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1266 if (record->col_text_len[text_col] > widest_column_len) {
1267 widest_column_str = record->col_text[text_col];
1268 widest_column_len = record->col_text_len[text_col];
1272 return widest_column_str;
1277 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1282 * indent-tabs-mode: t
1285 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1286 * :indentSize=8:tabSize=8:noTabs=false: