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 */
37 #include "packet_list_store.h"
39 #include "ui/progress_dlg.h"
40 #include "ui/ui_util.h"
42 #include "ui/gtk/old-gtk-compat.h"
44 #include <epan/epan_dissect.h>
45 #include <epan/column-info.h>
46 #include <epan/column.h>
49 #include "color_filters.h"
50 #include "frame_tvbuff.h"
54 /* #define PACKET_PARANOID_CHECKS */
56 /** PacketListRecord: represents a row */
57 typedef struct _PacketListRecord {
58 /** The column text for some columns */
59 const gchar **col_text;
60 /**< The length of the column text strings in 'col_text' */
61 gushort *col_text_len;
65 /* admin stuff used by the custom list model */
66 #ifdef PACKET_PARANOID_CHECKS
67 /** position within the physical array */
70 /** position within the visible array */
73 /** Has this record been colorized? */
78 static void packet_list_init(PacketList *pkg_tree);
79 static void packet_list_class_init(PacketListClass *klass);
80 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
81 static void packet_list_finalize(GObject *object);
82 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
83 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
84 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint idx);
85 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
86 GtkTreeIter *iter, GtkTreePath *path);
87 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
89 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
90 gint column, GValue *value);
91 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
93 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
96 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
97 GtkTreeIter *iter _U_);
98 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
100 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
104 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
105 GtkTreeIter *iter _U_,
106 GtkTreeIter *child _U_);
108 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
112 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
115 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
117 GtkTreeIterCompareFunc sort_func,
119 GDestroyNotify destroy_func);
120 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
122 GtkTreeIterCompareFunc
127 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
129 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
130 static void packet_list_resort(PacketList *packet_list);
131 static void packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color );
133 static GObjectClass *parent_class = NULL;
137 packet_list_get_type(void)
139 static GType packet_list_type = 0;
141 if(packet_list_type == 0) {
142 static const GTypeInfo packet_list_info = {
143 sizeof(PacketListClass),
144 NULL, /* base_init */
145 NULL, /* base_finalize */
146 (GClassInitFunc) packet_list_class_init,
147 NULL, /* class finalize */
148 NULL, /* class_data */
151 (GInstanceInitFunc) packet_list_init,
152 NULL /* value_table */
155 static const GInterfaceInfo tree_model_info = {
156 (GInterfaceInitFunc) packet_list_tree_model_init,
161 static const GInterfaceInfo tree_sortable_info = {
162 (GInterfaceInitFunc) packet_list_sortable_init,
167 /* Register the new derived type with the GObject type system */
168 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
173 g_type_add_interface_static(packet_list_type,
178 /* Register our GtkTreeModel interface with the type system */
179 g_type_add_interface_static(packet_list_type,
180 GTK_TYPE_TREE_SORTABLE,
181 &tree_sortable_info);
184 return packet_list_type;
188 packet_list_sortable_init(GtkTreeSortableIface *iface)
190 iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
191 iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
192 /* The following three functions are not implemented */
193 iface->set_sort_func = packet_list_sortable_set_sort_func;
194 iface->set_default_sort_func =
195 packet_list_sortable_set_default_sort_func;
196 iface->has_default_sort_func =
197 packet_list_sortable_has_default_sort_func;
201 packet_list_class_init(PacketListClass *klass)
203 GObjectClass *object_class;
205 parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
206 object_class = (GObjectClass*) klass;
208 object_class->finalize = packet_list_finalize;
210 #if !GTK_CHECK_VERSION(3,0,0)
211 /* XXX this seems to affect TreeView Application wide
212 * Move to main.c ??? as it's not a bad thing(tm)
214 gtk_rc_parse_string (
215 "style \"PacketList-style\"\n"
217 " GtkTreeView::horizontal-separator = 0\n"
218 " GtkTreeView::vertical-separator = 1\n"
219 "} widget_class \"*TreeView*\""
220 " style \"PacketList-style\"");
226 packet_list_tree_model_init(GtkTreeModelIface *iface)
228 iface->get_flags = packet_list_get_flags;
229 iface->get_n_columns = packet_list_get_n_columns;
230 iface->get_column_type = packet_list_get_column_type;
231 iface->get_iter = packet_list_get_iter;
232 iface->get_path = packet_list_get_path;
233 iface->get_value = packet_list_get_value;
234 iface->iter_next = packet_list_iter_next;
235 iface->iter_children = packet_list_iter_children;
236 iface->iter_has_child = packet_list_iter_has_child;
237 iface->iter_n_children = packet_list_iter_n_children;
238 iface->iter_nth_child = packet_list_iter_nth_child;
239 iface->iter_parent = packet_list_iter_parent;
242 /* This is called every time a new packet list object instance is created in
243 * packet_list_new. Initialize the list structure's fields here. */
245 packet_list_init(PacketList *packet_list)
249 /* To check whether an iter belongs to our model. */
250 packet_list->stamp = g_random_int();
252 packet_list->n_cols = cfile.cinfo.num_cols;
253 packet_list->physical_rows = g_ptr_array_new();
254 packet_list->visible_rows = g_ptr_array_new();
256 packet_list->columnized = FALSE;
257 packet_list->sort_id = 0; /* defaults to first column for now */
258 packet_list->sort_order = GTK_SORT_ASCENDING;
260 packet_list->col_to_text = g_new(int, packet_list->n_cols);
261 for (i = 0, j = 0; i < packet_list->n_cols; i++) {
262 if (!col_based_on_frame_data(&cfile.cinfo, i)) {
263 packet_list->col_to_text[i] = j;
266 packet_list->col_to_text[i] = -1;
268 packet_list->n_text_cols = j;
270 #ifdef PACKET_LIST_STATISTICS
271 packet_list->const_strings = 0;
275 /* This function is called just before a packet list is destroyed. Free
276 * dynamically allocated memory here. */
278 packet_list_finalize(GObject *object)
280 /* PacketList *packet_list = PACKET_LIST(object); */
282 /* XXX - Free all records and free all memory used by the list */
284 /* must chain up - finalize parent */
285 (* parent_class->finalize) (object);
288 static GtkTreeModelFlags
289 packet_list_get_flags(GtkTreeModel *tree_model)
291 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
292 (GtkTreeModelFlags)0);
294 return (GtkTreeModelFlags)(GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
298 packet_list_get_n_columns(GtkTreeModel *tree_model)
300 PacketList *packet_list;
302 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
303 packet_list = (PacketList *) tree_model;
305 /* Note: We need one extra column to store the entire frame_data */
306 return packet_list->n_cols + 1;
310 packet_list_get_column_type(GtkTreeModel *tree_model, gint idx)
312 PacketList *packet_list;
314 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
315 packet_list = (PacketList *) tree_model;
317 /* Note: We use one extra column to store the entire frame_data */
318 g_return_val_if_fail(idx >= 0 && idx < packet_list->n_cols + 1, G_TYPE_INVALID);
320 if (idx >= 0 && idx < packet_list->n_cols)
321 return G_TYPE_STRING;
322 else if (idx == packet_list->n_cols)
323 return G_TYPE_POINTER;
325 return G_TYPE_INVALID;
329 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
332 gint *indices, depth;
334 g_assert(PACKETLIST_IS_LIST(tree_model));
336 g_assert(path != NULL);
338 indices = gtk_tree_path_get_indices(path);
339 depth = gtk_tree_path_get_depth(path);
341 /* we do not allow children since it's just a list */
342 g_assert(depth == 1);
344 return packet_list_iter_nth_child(tree_model, iter, NULL, indices[0]);
348 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
351 PacketListRecord *record;
352 PacketList *packet_list;
354 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
355 packet_list = (PacketList *) tree_model;
357 g_return_val_if_fail(iter != NULL, NULL);
358 g_return_val_if_fail(iter->stamp == packet_list->stamp, NULL);
359 g_return_val_if_fail(iter->user_data != NULL, NULL);
361 record = (PacketListRecord*) iter->user_data;
363 path = gtk_tree_path_new();
364 gtk_tree_path_append_index(path, record->visible_pos);
370 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
373 PacketListRecord *record;
374 PacketList *packet_list;
376 g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
377 packet_list = (PacketList *) tree_model;
379 g_return_if_fail(iter != NULL);
380 g_return_if_fail(iter->stamp == packet_list->stamp);
381 g_return_if_fail(iter->user_data != NULL);
383 /* Note: We use one extra column to store the entire frame_data */
384 g_return_if_fail(column >= 0 && column < packet_list->n_cols + 1);
386 record = (PacketListRecord*) iter->user_data;
388 #ifdef PACKET_PARANOID_CHECKS
389 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->physical_rows, record->physical_pos));
391 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, record->visible_pos));
393 if (column >= 0 && column < packet_list->n_cols) {
396 g_value_init(value, G_TYPE_STRING);
398 if (record->col_text == NULL || !record->colorized)
399 packet_list_dissect_and_cache_record(packet_list, record, !record->colorized);
401 text_column = packet_list->col_to_text[column];
402 if (text_column == -1) { /* column based on frame_data */
403 col_fill_in_frame_data(record->fdata, &cfile.cinfo, column, FALSE);
404 g_value_set_string(value, cfile.cinfo.col_data[column]);
406 g_return_if_fail(record->col_text);
407 g_value_set_string(value, record->col_text[text_column]);
410 } else if (column == packet_list->n_cols) {
411 g_value_init(value, G_TYPE_POINTER);
412 g_value_set_pointer(value, record->fdata);
416 static PacketListRecord *
417 packet_list_iter_next_visible(PacketList *packet_list, PacketListRecord *record)
419 PacketListRecord *nextrecord;
420 gint next_visible_pos;
422 g_assert(record->visible_pos >= 0);
423 next_visible_pos = record->visible_pos + 1;
425 /* Is this the last record in the list? */
426 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, next_visible_pos))
429 nextrecord = PACKET_LIST_RECORD_GET(packet_list->visible_rows, next_visible_pos);
431 g_assert(nextrecord->visible_pos == (record->visible_pos + 1));
432 #ifdef PACKET_PARANOID_CHECKS
433 g_assert(nextrecord->physical_pos >= (record->physical_pos + 1));
439 /* Takes an iter structure and sets it to point to the next row. */
441 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
443 PacketListRecord *record, *nextrecord;
444 PacketList *packet_list;
446 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
447 packet_list = (PacketList *) tree_model;
452 g_return_val_if_fail(iter->stamp == packet_list->stamp, FALSE);
453 g_return_val_if_fail(iter->user_data, FALSE);
455 record = (PacketListRecord*) iter->user_data;
456 nextrecord = packet_list_iter_next_visible(packet_list, record);
461 /* iter->stamp = packet_list->stamp; */
462 iter->user_data = nextrecord;
468 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
471 return packet_list_iter_nth_child(tree_model, iter, parent, 0);
475 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
477 return FALSE; /* Lists have no children */
481 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
483 PacketList *packet_list;
485 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
486 packet_list = (PacketList *) tree_model;
489 /* special case: if iter == NULL, return number of top-level rows */
490 return PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
493 g_return_val_if_fail(iter->stamp == packet_list->stamp, 0);
494 g_return_val_if_fail(iter->user_data, 0);
495 /* Lists have zero children */
501 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
502 GtkTreeIter *parent, gint n)
504 PacketListRecord *record;
505 PacketList *packet_list;
507 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
508 packet_list = (PacketList *) tree_model;
510 /* A list only has top-level rows */
512 g_return_val_if_fail(parent->stamp == packet_list->stamp, FALSE);
513 g_return_val_if_fail(parent->user_data, FALSE);
517 /* Special case: if parent == NULL, set iter to n-th top-level row. */
518 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
521 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
523 g_assert(record->visible_pos == n);
525 iter->stamp = packet_list->stamp;
526 iter->user_data = record;
532 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
533 GtkTreeIter *child _U_)
535 return FALSE; /* No parents since no children in a list */
539 packet_list_new(void)
541 PacketList *newpacketlist;
543 newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
545 g_assert(newpacketlist != NULL);
547 return newpacketlist;
552 packet_list_row_deleted(PacketList *packet_list, guint pos)
556 /* Inform the tree view and other interested objects (such as tree row
557 * references) that we have deleted a row */
558 path = gtk_tree_path_new();
559 gtk_tree_path_append_index(path, pos);
561 gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
563 gtk_tree_path_free(path);
568 packet_list_store_clear(PacketList *packet_list)
570 g_return_if_fail(packet_list != NULL);
571 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
573 /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
574 * the model from the view.
575 for( ; packet_list->num_rows > 0; --packet_list->num_rows)
576 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
579 /* XXX - hold on to these rows and reuse them instead */
580 if(packet_list->physical_rows)
581 g_ptr_array_free(packet_list->physical_rows, TRUE);
582 if(packet_list->visible_rows)
583 g_ptr_array_free(packet_list->visible_rows, TRUE);
584 packet_list->physical_rows = g_ptr_array_new();
585 packet_list->visible_rows = g_ptr_array_new();
587 packet_list->columnized = FALSE;
589 /* Generate new number */
590 packet_list->stamp = g_random_int();
592 #ifdef PACKET_LIST_STATISTICS
593 g_warning("Const strings: %u", packet_list->const_strings);
594 packet_list->const_strings = 0;
599 packet_list_append_record(PacketList *packet_list, frame_data *fdata)
601 PacketListRecord *newrecord;
603 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), -1);
605 newrecord = se_new(PacketListRecord);
606 newrecord->colorized = FALSE;
607 newrecord->col_text_len = NULL;
608 newrecord->col_text = NULL;
609 newrecord->fdata = fdata;
610 #ifdef PACKET_PARANOID_CHECKS
611 newrecord->physical_pos = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
614 if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
615 newrecord->visible_pos = PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
616 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, newrecord);
619 newrecord->visible_pos = -1;
621 PACKET_LIST_RECORD_APPEND(packet_list->physical_rows, newrecord);
623 packet_list->columnized = FALSE; /* XXX, dissect? */
626 * Issue a row_inserted signal if the model is connected
627 * and the row is visible.
629 if (gtk_tree_view_get_model(GTK_TREE_VIEW(packet_list->view)) && newrecord->visible_pos != -1) {
633 path = gtk_tree_path_new();
634 gtk_tree_path_append_index(path, newrecord->visible_pos);
636 iter.stamp = packet_list->stamp;
637 iter.user_data = newrecord;
639 gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
640 gtk_tree_path_free(path);
643 /* XXXX If the model is connected and sort column != frame_num we should
645 * Don't resort the list for every row, the list will be in packet order any way.
646 * packet_list_resort(packet_list);
649 return newrecord->visible_pos;
653 packet_list_change_record(PacketList *packet_list, PacketListRecord *record, gint col, column_info *cinfo)
659 text_col = packet_list->col_to_text[col];
661 /* Column based on frame_data or it already contains a value */
662 if (text_col == -1 || record->col_text[text_col] != NULL)
665 switch (cfile.cinfo.col_fmt[col]) {
667 case COL_RES_SRC: /* COL_DEF_SRC is currently just like COL_RES_SRC */
671 case COL_UNRES_DL_SRC:
672 case COL_DEF_NET_SRC:
673 case COL_RES_NET_SRC:
674 case COL_UNRES_NET_SRC:
676 case COL_RES_DST: /* COL_DEF_DST is currently just like COL_RES_DST */
680 case COL_UNRES_DL_DST:
681 case COL_DEF_NET_DST:
682 case COL_RES_NET_DST:
683 case COL_UNRES_NET_DST:
688 case COL_8021Q_VLAN_ID:
691 if (cinfo->col_data[col] && cinfo->col_data[col] != cinfo->col_buf[col]) {
692 col_text_len = strlen(cinfo->col_data[col]);
693 if (col_text_len > G_MAXUSHORT)
694 col_text_len = G_MAXUSHORT;
696 /* This is a constant string, so we don't have to copy it */
697 record->col_text[text_col] = (gchar *) cinfo->col_data[col];
698 record->col_text_len[text_col] = (gushort) col_text_len;
699 #ifdef PACKET_LIST_STATISTICS
700 ++packet_list->const_strings;
704 /* !! FALL-THROUGH!! */
707 if(cinfo->col_data[col]){
708 col_text_len = strlen(cinfo->col_data[col]);
709 if (col_text_len > G_MAXUSHORT)
710 col_text_len = G_MAXUSHORT;
712 record->col_text_len[text_col] = (gushort) col_text_len;
714 if (!record->col_text_len[text_col]) {
715 record->col_text[text_col] = "";
716 #ifdef PACKET_LIST_STATISTICS
717 ++packet_list->const_strings;
722 if(!packet_list->string_pool)
723 packet_list->string_pool = g_string_chunk_new(32);
724 if (!get_column_resolved (col) && cinfo->col_expr.col_expr_val[col]) {
725 /* Use the unresolved value in col_expr_val */
726 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_expr.col_expr_val[col]);
728 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_data[col]);
730 record->col_text[text_col] = str;
736 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
740 PacketList *packet_list;
742 g_return_val_if_fail(sortable != NULL, FALSE);
743 g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
745 packet_list = (PacketList *) sortable;
748 *sort_col_id = packet_list->sort_id;
751 *order = packet_list->sort_order;
757 packet_list_column_contains_values(PacketList *packet_list, gint sort_col_id)
759 if (packet_list->columnized || col_based_on_frame_data(&cfile.cinfo, sort_col_id))
765 /* packet_list_dissect_and_cache_all()
767 * TRUE if columnization completed;
768 * packet_list->columnized set to TRUE;
769 * FALSE: columnization did not complete (i.e., was stopped by the user);
770 * packet_list->columnized unchanged (i.e., FALSE).
774 packet_list_dissect_and_cache_all(PacketList *packet_list)
776 PacketListRecord *record;
778 int progbar_nextstep;
780 gboolean progbar_stop_flag;
781 GTimeVal progbar_start_time;
783 progdlg_t *progbar = NULL;
784 gchar progbar_status_str[100];
785 gint progbar_loop_max;
786 gint progbar_loop_var;
787 gint progbar_updates = 100 /* 100% */;
789 g_assert(packet_list->columnized == FALSE);
791 progbar_loop_max = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
792 /* Update the progress bar when it gets to this value. */
793 progbar_nextstep = 0;
794 /* When we reach the value that triggers a progress bar update,
795 bump that value by this amount. */
796 progbar_quantum = progbar_loop_max/progbar_updates;
797 /* Progress so far. */
800 progbar_stop_flag = FALSE;
801 g_get_current_time(&progbar_start_time);
803 main_window_update();
805 for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var) {
806 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, progbar_loop_var);
807 packet_list_dissect_and_cache_record(packet_list, record, FALSE);
809 /* Create the progress bar if necessary.
810 We check on every iteration of the loop, so that it takes no
811 longer than the standard time to create it (otherwise, for a
812 large file, we might take considerably longer than that standard
813 time in order to get to the next progress bar step). */
815 /* Note: The following may call gtk_main_iteration() which will */
816 /* allow certain "interupts" to happen during this code. */
817 /* (Note that the progress_dlg window is set to "modal" */
818 /* so that clicking on other windows is disabled). */
819 progbar = delayed_create_progress_dlg(gtk_widget_get_window(packet_list->view),
820 "Construct", "Columns",
821 TRUE, &progbar_stop_flag,
822 &progbar_start_time, progbar_val);
824 if (progbar_loop_var >= progbar_nextstep) {
825 /* let's not divide by zero. We should never be started
826 * with count == 0, so let's assert that */
827 g_assert(progbar_loop_max > 0);
829 progbar_val = (gfloat) progbar_loop_var / progbar_loop_max;
831 if (progbar != NULL) {
832 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
833 "%u of %u frames", progbar_loop_var+1, progbar_loop_max);
834 /* Note: See comment above re use of gtk_main_iteration() */
835 update_progress_dlg(progbar, progbar_val, progbar_status_str);
838 progbar_nextstep += progbar_quantum;
841 if (progbar_stop_flag) {
842 /* Well, the user decided to abort ... */
847 /* We're done; destroy the progress bar if it was created. */
849 destroy_progress_dlg(progbar);
851 if (progbar_stop_flag) {
852 return FALSE; /* user aborted before columnization completed */
855 packet_list->columnized = TRUE;
859 /* packet_list_do_packet_list_dissect_and_cache_all()
861 * TRUE: if columnization not needed or columnization completed;
862 * FALSE: columnization did not complete (i.e., stopped by the user)
865 packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id)
867 if (!packet_list_column_contains_values(packet_list, sort_col_id)) {
868 return packet_list_dissect_and_cache_all(packet_list);
874 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
878 PacketList *packet_list;
880 g_return_if_fail(sortable != NULL);
881 g_return_if_fail(PACKETLIST_IS_LIST(sortable));
883 packet_list = (PacketList *) sortable;
885 if(packet_list->sort_id == sort_col_id &&
886 packet_list->sort_order == order)
889 packet_list->sort_id = sort_col_id;
890 packet_list->sort_order = order;
892 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
895 packet_list_resort(packet_list);
897 /* emit "sort-column-changed" signal to tell any tree views
898 * that the sort column has changed (so the little arrow
899 * in the column header of the sort column is drawn
900 * in the right column) */
902 gtk_tree_sortable_sort_column_changed(sortable);
906 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
907 gint sort_col_id _U_,
908 GtkTreeIterCompareFunc sort_func _U_,
909 gpointer user_data _U_,
910 GDestroyNotify destroy_func _U_)
912 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
916 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
917 GtkTreeIterCompareFunc sort_func _U_,
918 gpointer user_data _U_,
919 GDestroyNotify destroy_func _U_)
921 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
925 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
927 return FALSE; /* Since packet_list_sortable_set_sort_func and
928 set_default_sort_func are not implemented. */
932 packet_list_compare_custom(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
934 header_field_info *hfi;
936 hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[sort_id]);
939 return frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
940 } else if ((hfi->strings == NULL) &&
941 (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
942 ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
943 (hfi->display == BASE_OCT))) ||
944 (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
945 (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
946 (hfi->type == FT_RELATIVE_TIME)))
948 /* Attempt to convert to numbers */
949 double num_a = atof(a->col_text[text_sort_id]);
950 double num_b = atof(b->col_text[text_sort_id]);
954 else if (num_a > num_b)
960 return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
964 _packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
966 g_assert(a->col_text);
967 g_assert(b->col_text);
968 g_assert(a->col_text[text_sort_id]);
969 g_assert(b->col_text[text_sort_id]);
971 if(a->col_text[text_sort_id] == b->col_text[text_sort_id])
972 return 0; /* no need to call strcmp() */
974 if (cfile.cinfo.col_fmt[sort_id] == COL_CUSTOM)
975 return packet_list_compare_custom(sort_id, text_sort_id, a, b);
977 return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
981 packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
985 if (text_sort_id == -1) /* based on frame_data ? */
986 return frame_data_compare(cfile.epan, a->fdata, b->fdata, cfile.cinfo.col_fmt[sort_id]);
988 ret = _packet_list_compare_records(sort_id, text_sort_id, a, b);
990 ret = frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
995 packet_list_qsort_physical_compare_func(PacketListRecord **a, PacketListRecord **b,
996 PacketList *packet_list)
999 gint sort_id = packet_list->sort_id;
1001 g_assert((a) && (b) && (packet_list));
1003 ret = packet_list_compare_records(sort_id, packet_list->col_to_text[sort_id], *a, *b);
1005 /* Swap -1 and 1 if sort order is reverse */
1006 if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
1007 ret = (ret < 0) ? 1 : -1;
1013 packet_list_resort(PacketList *packet_list)
1015 PacketListRecord *record;
1021 g_return_if_fail(packet_list != NULL);
1022 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1024 if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1027 /* resort physical rows according to sorting column */
1028 g_ptr_array_sort_with_data(packet_list->physical_rows,
1029 (GCompareDataFunc) packet_list_qsort_physical_compare_func,
1032 /* let other objects know about the new order */
1033 neworder = g_new0(gint, PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1035 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1036 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1037 #ifdef PACKET_PARANOID_CHECKS
1038 record->physical_pos = phy_idx;
1040 g_assert(record->visible_pos >= -1);
1041 if (record->visible_pos >= 0) {
1042 g_assert(record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
1043 neworder[vis_idx] = record->visible_pos;
1044 PACKET_LIST_RECORD_SET(packet_list->visible_rows, vis_idx, record);
1045 record->visible_pos = vis_idx;
1050 g_assert(vis_idx == PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1052 path = gtk_tree_path_new();
1054 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
1057 gtk_tree_path_free(path);
1062 packet_list_recreate_visible_rows_list(PacketList *packet_list)
1066 PacketListRecord *record;
1068 g_return_val_if_fail(packet_list != NULL, 0);
1069 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), 0);
1071 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
1074 if(packet_list->visible_rows)
1075 g_ptr_array_free(packet_list->visible_rows, TRUE);
1077 packet_list->visible_rows = g_ptr_array_new();
1079 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1080 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1081 if (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time) {
1082 record->visible_pos = vis_idx++;
1083 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, record);
1086 record->visible_pos = -1;
1093 packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color)
1099 gboolean create_proto_tree;
1100 struct wtap_pkthdr phdr; /* Packet header */
1101 Buffer buf; /* Packet data */
1102 gboolean dissect_columns = (record->col_text == NULL);
1104 g_return_if_fail(packet_list);
1105 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1107 wtap_phdr_init(&phdr);
1109 fdata = record->fdata;
1111 if (dissect_columns) {
1112 cinfo = &cfile.cinfo;
1114 record->col_text = (const gchar **)se_alloc0(sizeof(*record->col_text) * packet_list->n_text_cols);
1115 record->col_text_len = (gushort *)se_alloc0(sizeof(*record->col_text_len) * packet_list->n_text_cols);
1119 ws_buffer_init(&buf, 1500);
1120 if (!cf_read_record_r(&cfile, fdata, &phdr, &buf)) {
1122 * Error reading the record.
1124 * Don't set the color filter for now (we might want
1125 * to colorize it in some fashion to warn that the
1126 * row couldn't be filled in or colorized), and
1127 * set the columns to placeholder values, except
1128 * for the Info column, where we'll put in an
1131 if (dissect_columns) {
1132 col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
1134 for(col = 0; col < cinfo->num_cols; ++col)
1135 packet_list_change_record(packet_list, record, col, cinfo);
1137 if (dissect_color) {
1138 fdata->color_filter = NULL;
1139 record->colorized = TRUE;
1141 ws_buffer_free(&buf);
1142 return; /* error reading the record */
1145 create_proto_tree = (dissect_color && color_filters_used()) ||
1146 (dissect_columns && have_custom_cols(cinfo));
1148 epan_dissect_init(&edt, cfile.epan,
1150 FALSE /* proto_tree_visible */);
1153 color_filters_prime_edt(&edt);
1154 if (dissect_columns)
1155 col_custom_prime_edt(&edt, cinfo);
1158 * XXX - need to catch an OutOfMemoryError exception and
1159 * attempt to recover from it.
1161 epan_dissect_run(&edt, cfile.cd_t, &phdr, frame_tvbuff_new_buffer(fdata, &buf), fdata, cinfo);
1164 fdata->color_filter = color_filters_colorize_packet(&edt);
1166 if (dissect_columns) {
1167 /* "Stringify" non frame_data vals */
1168 epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */);
1170 for(col = 0; col < cinfo->num_cols; ++col)
1171 packet_list_change_record(packet_list, record, col, cinfo);
1175 record->colorized = TRUE;
1177 epan_dissect_cleanup(&edt);
1178 wtap_phdr_cleanup(&phdr);
1179 ws_buffer_free(&buf);
1183 packet_list_reset_colorized(PacketList *packet_list)
1185 PacketListRecord *record;
1188 for(i = 0; i < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++i) {
1189 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, i);
1190 record->colorized = FALSE;
1195 packet_list_get_widest_column_string(PacketList *packet_list, gint col)
1199 g_return_val_if_fail(packet_list != NULL, NULL);
1200 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), NULL);
1201 /* We need real column here, so not packet_list->n_cols+1 */
1202 g_return_val_if_fail(col >= 0 && col < packet_list->n_cols, NULL);
1204 if (PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1207 text_col = packet_list->col_to_text[col];
1209 if (text_col == -1) { /* column based on frame data */
1210 PacketListRecord *record;
1213 guint widest_packet = 0;
1214 gint widest_column_len = -1;
1216 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1219 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1221 col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1222 column_len = (gint) strlen(cfile.cinfo.col_buf[col]);
1223 if (column_len > widest_column_len) {
1224 widest_column_len = column_len;
1225 widest_packet = vis_idx;
1229 if (widest_column_len != -1) {
1230 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, widest_packet);
1231 col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1233 return cfile.cinfo.col_buf[col];
1238 PacketListRecord *record;
1241 const gchar *widest_column_str = NULL;
1242 guint widest_column_len = 0;
1244 if (!packet_list->columnized)
1245 packet_list_dissect_and_cache_all(packet_list); /* XXX: need to handle case of "incomplete" ? */
1247 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1248 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1249 if (record->col_text_len[text_col] > widest_column_len) {
1250 widest_column_str = record->col_text[text_col];
1251 widest_column_len = record->col_text_len[text_col];
1255 return widest_column_str;