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.
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
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.
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.
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,
28 /* This code was originally based on the GTK+ Tree View tutorial at
29 * http://scentric.net/tutorial */
40 #include "packet_list_store.h"
43 #include <epan/epan_dissect.h>
44 #include <epan/column_info.h>
45 #include <epan/column.h>
46 #include <epan/nstime.h>
49 #include "color_filters.h"
53 #include "../progress_dlg.h"
55 static void packet_list_init(PacketList *pkg_tree);
56 static void packet_list_class_init(PacketListClass *klass);
57 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
58 static void packet_list_finalize(GObject *object);
59 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
60 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
61 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint idx);
62 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
63 GtkTreeIter *iter, GtkTreePath *path);
64 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
66 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
67 gint column, GValue *value);
68 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
70 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
73 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
74 GtkTreeIter *iter _U_);
75 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
77 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
81 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
82 GtkTreeIter *iter _U_,
83 GtkTreeIter *child _U_);
85 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
89 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
92 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
94 GtkTreeIterCompareFunc sort_func,
96 GDestroyNotify destroy_func);
97 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
99 GtkTreeIterCompareFunc
104 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
106 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
107 static gint packet_list_compare_records(gint sort_id _U_, PacketListRecord *a,
108 PacketListRecord *b);
109 static void packet_list_resort(PacketList *packet_list);
110 static void packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_columns, gboolean dissect_color );
112 static GObjectClass *parent_class = NULL;
116 packet_list_get_type(void)
118 static GType packet_list_type = 0;
120 if(packet_list_type == 0) {
121 static const GTypeInfo packet_list_info = {
122 sizeof(PacketListClass),
123 NULL, /* base_init */
124 NULL, /* base_finalize */
125 (GClassInitFunc) packet_list_class_init,
126 NULL, /* class finalize */
127 NULL, /* class_data */
130 (GInstanceInitFunc) packet_list_init,
131 NULL /* value_table */
134 static const GInterfaceInfo tree_model_info = {
135 (GInterfaceInitFunc) packet_list_tree_model_init,
140 static const GInterfaceInfo tree_sortable_info = {
141 (GInterfaceInitFunc) packet_list_sortable_init,
146 /* Register the new derived type with the GObject type system */
147 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
152 g_type_add_interface_static(packet_list_type,
157 /* Register our GtkTreeModel interface with the type system */
158 g_type_add_interface_static(packet_list_type,
159 GTK_TYPE_TREE_SORTABLE,
160 &tree_sortable_info);
163 return packet_list_type;
167 packet_list_sortable_init(GtkTreeSortableIface *iface)
169 iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
170 iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
171 /* The following three functions are not implemented */
172 iface->set_sort_func = packet_list_sortable_set_sort_func;
173 iface->set_default_sort_func =
174 packet_list_sortable_set_default_sort_func;
175 iface->has_default_sort_func =
176 packet_list_sortable_has_default_sort_func;
180 packet_list_class_init(PacketListClass *klass)
182 GObjectClass *object_class;
184 parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
185 object_class = (GObjectClass*) klass;
187 object_class->finalize = packet_list_finalize;
189 #if GTK_CHECK_VERSION(3,0,0)
191 /* XXX this seems to affect TreeView Application wide
192 * Move to main.c ??? as it's not a bad thing(tm)
194 gtk_rc_parse_string (
195 "style \"PacketList-style\"\n"
197 " GtkTreeView::horizontal-separator = 0\n"
198 " GtkTreeView::vertical-separator = 1\n"
199 "} widget_class \"*TreeView*\""
200 " style \"PacketList-style\"");
206 packet_list_tree_model_init(GtkTreeModelIface *iface)
208 iface->get_flags = packet_list_get_flags;
209 iface->get_n_columns = packet_list_get_n_columns;
210 iface->get_column_type = packet_list_get_column_type;
211 iface->get_iter = packet_list_get_iter;
212 iface->get_path = packet_list_get_path;
213 iface->get_value = packet_list_get_value;
214 iface->iter_next = packet_list_iter_next;
215 iface->iter_children = packet_list_iter_children;
216 iface->iter_has_child = packet_list_iter_has_child;
217 iface->iter_n_children = packet_list_iter_n_children;
218 iface->iter_nth_child = packet_list_iter_nth_child;
219 iface->iter_parent = packet_list_iter_parent;
222 /* This is called every time a new packet list object instance is created in
223 * packet_list_new. Initialize the list structure's fields here. */
225 packet_list_init(PacketList *packet_list)
229 for(i = 0; i < (guint)cfile.cinfo.num_cols; i++) {
230 /* We get the packetlist record for all columns */
231 packet_list->column_types[i] = G_TYPE_STRING;
234 /* To check whether an iter belongs to our model. */
235 packet_list->stamp = g_random_int();
237 /* Note: We need one extra column to store the entire PacketListRecord */
238 packet_list->column_types[i] = G_TYPE_POINTER;
239 packet_list->n_columns = (guint)cfile.cinfo.num_cols+1;
240 packet_list->physical_rows = g_ptr_array_new();
241 packet_list->visible_rows = g_ptr_array_new();
243 packet_list->columnized = FALSE;
244 packet_list->sort_id = 0; /* defaults to first column for now */
245 packet_list->sort_order = GTK_SORT_ASCENDING;
247 #ifdef NEW_PACKET_LIST_STATISTICS
248 packet_list->const_strings = 0;
252 /* This function is called just before a packet list is destroyed. Free
253 * dynamically allocated memory here. */
255 packet_list_finalize(GObject *object)
257 /* PacketList *packet_list = PACKET_LIST(object); */
259 /* XXX - Free all records and free all memory used by the list */
261 /* must chain up - finalize parent */
262 (* parent_class->finalize) (object);
265 static GtkTreeModelFlags
266 packet_list_get_flags(GtkTreeModel *tree_model)
268 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
269 (GtkTreeModelFlags)0);
271 return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
275 packet_list_get_n_columns(GtkTreeModel *tree_model)
277 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
279 return PACKET_LIST(tree_model)->n_columns;
283 packet_list_get_column_type(GtkTreeModel *tree_model, gint idx)
285 PacketList *packet_list;
286 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
287 packet_list = PACKET_LIST(tree_model);
288 /* Note: We use one extra column to store the entire PacketListRecord */
289 g_return_val_if_fail(idx < packet_list->n_columns &&
290 idx >= 0, G_TYPE_INVALID);
292 return packet_list->column_types[idx];
296 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
299 PacketList *packet_list;
300 PacketListRecord *record;
301 gint *indices, depth;
304 g_assert(PACKETLIST_IS_LIST(tree_model));
305 g_assert(path != NULL);
307 indices = gtk_tree_path_get_indices(path);
308 depth = gtk_tree_path_get_depth(path);
310 /* we do not allow children since it's just a list */
311 g_assert(depth == 1);
313 n = indices[0]; /* the n-th top level row */
315 packet_list = PACKET_LIST(tree_model);
316 if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
319 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
322 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
324 g_assert(record->visible_pos == n);
326 /* We simply store a pointer to our custom record in the iter */
327 iter->stamp = packet_list->stamp;
328 iter->user_data = record;
329 iter->user_data2 = NULL;
330 iter->user_data3 = NULL;
336 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
339 PacketListRecord *record;
341 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
342 g_return_val_if_fail(iter != NULL, NULL);
343 g_return_val_if_fail(iter->user_data != NULL, NULL);
345 record = (PacketListRecord*) iter->user_data;
347 path = gtk_tree_path_new();
348 gtk_tree_path_append_index(path, record->visible_pos);
354 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
357 PacketListRecord *record;
358 PacketList *packet_list;
361 g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
362 g_return_if_fail(iter != NULL);
364 packet_list = PACKET_LIST(tree_model);
365 /* Note: We use one extra column to store the entire PacketListRecord */
366 g_return_if_fail(column < packet_list->n_columns);
368 record = (PacketListRecord*) iter->user_data;
370 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->physical_rows, record->physical_pos));
371 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, record->visible_pos));
373 type = packet_list->column_types[column];
374 g_value_init(value, type);
376 /* XXX Probably the switch should be on column or
377 * should we allways return the pointer and read the data as required??
378 * If we use FOREGROUND_COLOR_COL etc we'll need a couple of "internal" columns
382 g_value_set_pointer(value, record);
385 g_return_if_fail(record->col_text);
386 g_value_set_string(value, record->col_text[column]);
389 g_warning (G_STRLOC ": Unsupported type (%s) retrieved.", g_type_name (value->g_type));
394 static PacketListRecord *
395 packet_list_iter_next_visible(PacketList *packet_list, PacketListRecord *record)
397 PacketListRecord *nextrecord;
398 gint next_visible_pos;
400 g_assert(record->visible_pos >= 0);
401 next_visible_pos = record->visible_pos + 1;
403 /* Is this the last record in the list? */
404 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, next_visible_pos))
407 nextrecord = PACKET_LIST_RECORD_GET(packet_list->visible_rows, next_visible_pos);
409 g_assert(nextrecord->visible_pos == (record->visible_pos + 1));
410 g_assert(nextrecord->physical_pos >= (record->physical_pos + 1));
415 /* Takes an iter structure and sets it to point to the next row. */
417 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
419 PacketListRecord *record, *nextrecord;
420 PacketList *packet_list;
422 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
427 g_return_val_if_fail(iter->user_data, FALSE);
429 packet_list = PACKET_LIST(tree_model);
431 record = (PacketListRecord*) iter->user_data;
432 nextrecord = packet_list_iter_next_visible(packet_list, record);
437 iter->stamp = packet_list->stamp;
438 iter->user_data = nextrecord;
444 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
447 PacketList *packet_list;
449 g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
451 /* This is a list, nodes have no children. */
455 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
457 packet_list = PACKET_LIST(tree_model);
459 /* No rows => no first row */
460 if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
463 /* Set iter to first item in list */
464 iter->stamp = packet_list->stamp;
465 iter->user_data = PACKET_LIST_RECORD_GET(packet_list->visible_rows, 0);
471 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
473 return FALSE; /* Lists have no children */
477 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
479 PacketList *packet_list;
481 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
482 g_return_val_if_fail(iter == NULL || iter->user_data != NULL, 0);
484 packet_list = PACKET_LIST(tree_model);
487 /* special case: if iter == NULL, return number of top-level rows */
488 return PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
491 /* Lists have zero children */
497 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
498 GtkTreeIter *parent, gint n)
500 PacketListRecord *record;
501 PacketList *packet_list;
503 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
505 packet_list = PACKET_LIST(tree_model);
507 /* A list only has top-level rows */
511 /* Special case: if parent == NULL, set iter to n-th top-level row. */
512 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
515 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
517 g_assert(record->visible_pos == n);
519 iter->stamp = packet_list->stamp;
520 iter->user_data = record;
526 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
527 GtkTreeIter *child _U_)
529 return FALSE; /* No parents since no children in a list */
533 new_packet_list_new(void)
535 PacketList *newpacketlist;
537 newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
539 g_assert(newpacketlist != NULL);
541 return newpacketlist;
546 packet_list_row_deleted(PacketList *packet_list, guint pos)
550 /* Inform the tree view and other interested objects (such as tree row
551 * references) that we have deleted a row */
552 path = gtk_tree_path_new();
553 gtk_tree_path_append_index(path, pos);
555 gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
557 gtk_tree_path_free(path);
562 new_packet_list_store_clear(PacketList *packet_list)
564 g_return_if_fail(packet_list != NULL);
565 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
567 /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
568 * the model from the view.
569 for( ; packet_list->num_rows > 0; --packet_list->num_rows)
570 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
573 /* XXX - hold on to these rows and reuse them instead */
574 if(packet_list->physical_rows)
575 g_ptr_array_free(packet_list->physical_rows, TRUE);
576 if(packet_list->visible_rows)
577 g_ptr_array_free(packet_list->visible_rows, TRUE);
578 packet_list->physical_rows = g_ptr_array_new();
579 packet_list->visible_rows = g_ptr_array_new();
581 packet_list->columnized = FALSE;
583 #ifdef NEW_PACKET_LIST_STATISTICS
584 g_warning("Const strings: %u", packet_list->const_strings);
585 packet_list->const_strings = 0;
590 packet_list_row_inserted(PacketList *packet_list, guint pos)
595 /* Inform the tree view and other interested objects (such as tree row
596 * references) that we have inserted a new row and where it was
598 path = gtk_tree_path_new();
599 gtk_tree_path_append_index(path, pos);
601 packet_list_get_iter(GTK_TREE_MODEL(packet_list), &iter, path);
603 gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
605 gtk_tree_path_free(path);
609 packet_list_visible_record(PacketList *packet_list, GtkTreeIter *iter)
611 PacketListRecord *record;
613 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), FALSE);
615 if(iter == NULL || iter->user_data == NULL)
618 record = (PacketListRecord*) iter->user_data;
620 g_return_val_if_fail(record, FALSE);
621 g_return_val_if_fail(record->fdata, FALSE);
623 return (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
627 packet_list_append_record(PacketList *packet_list, frame_data *fdata)
629 PacketListRecord *newrecord;
630 GtkTreeModel *model = GTK_TREE_MODEL(packet_list);
632 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), -1);
634 newrecord = se_alloc(sizeof(PacketListRecord));
635 newrecord->columnized = FALSE;
636 newrecord->colorized = FALSE;
637 newrecord->col_text_len = se_alloc0(sizeof(*newrecord->col_text_len) * cfile.cinfo.num_cols);
638 newrecord->col_text = se_alloc0(sizeof(*newrecord->col_text) * cfile.cinfo.num_cols);
639 newrecord->fdata = fdata;
640 newrecord->physical_pos = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
642 if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
643 newrecord->visible_pos = PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
644 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, newrecord);
647 newrecord->visible_pos = -1;
649 PACKET_LIST_RECORD_APPEND(packet_list->physical_rows, newrecord);
651 packet_list->columnized = FALSE; /* XXX, dissect? */
654 * Issue a row_inserted signal if the model is connected
655 * and the row is visible.
657 if((model)&&(newrecord->visible_pos!=-1))
658 packet_list_row_inserted(packet_list, newrecord->visible_pos);
660 /* XXXX If the model is connected and sort column != frame_num we should
662 * Don't resort the list for every row, the list will be in packet order any way.
663 * packet_list_resort(packet_list);
666 return newrecord->visible_pos;
670 packet_list_change_record(PacketList *packet_list, guint row, gint col, column_info *cinfo)
672 PacketListRecord *record;
675 g_return_if_fail(packet_list);
676 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
678 g_assert(row < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows));
680 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, row);
682 g_assert(record->physical_pos == row);
684 g_assert((record->col_text != NULL)&&(record->col_text_len != NULL));
686 if (record->col_text[col] != NULL)
687 /* TODO: Column already contains a value. Bail out */
690 switch (cfile.cinfo.col_fmt[col]) {
692 case COL_RES_SRC: /* COL_DEF_SRC is currently just like COL_RES_SRC */
696 case COL_UNRES_DL_SRC:
697 case COL_DEF_NET_SRC:
698 case COL_RES_NET_SRC:
699 case COL_UNRES_NET_SRC:
701 case COL_RES_DST: /* COL_DEF_DST is currently just like COL_RES_DST */
705 case COL_UNRES_DL_DST:
706 case COL_DEF_NET_DST:
707 case COL_RES_NET_DST:
708 case COL_UNRES_NET_DST:
713 case COL_8021Q_VLAN_ID:
716 if (cinfo->col_data[col] && cinfo->col_data[col] != cinfo->col_buf[col]) {
717 /* This is a constant string, so we don't have to copy it */
718 record->col_text[col] = (gchar *) cinfo->col_data[col];
719 record->col_text_len[col] = (guint) strlen(record->col_text[col]);
720 #ifdef NEW_PACKET_LIST_STATISTICS
721 ++packet_list->const_strings;
725 /* !! FALL-THROUGH!! */
728 record->col_text_len[col] = (guint) strlen(cinfo->col_data[col]);
730 if (!record->col_text_len[col]) {
731 record->col_text[col] = "";
732 #ifdef NEW_PACKET_LIST_STATISTICS
733 ++packet_list->const_strings;
738 if(!packet_list->string_pool)
739 packet_list->string_pool = g_string_chunk_new(32);
740 if (!get_column_resolved (col) && cinfo->col_expr.col_expr_val[col]) {
741 /* Use the unresolved value in col_expr_val */
742 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_expr.col_expr_val[col]);
744 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_data[col]);
746 record->col_text[col] = str;
752 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
756 PacketList *packet_list;
758 g_return_val_if_fail(sortable != NULL, FALSE);
759 g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
761 packet_list = PACKET_LIST(sortable);
764 *sort_col_id = packet_list->sort_id;
767 *order = packet_list->sort_order;
773 packet_list_column_contains_values(PacketList *packet_list, gint sort_col_id)
775 if (packet_list->columnized || col_based_on_frame_data(&cfile.cinfo, sort_col_id))
781 /* packet_list_dissect_and_cache_all()
783 * TRUE if columnization completed;
784 * packet_list->columnized set to TRUE;
785 * FALSE: columnization did not complete (i.e., was stopped by the user);
786 * packet_list->columnized unchanged (i.e., FALSE).
790 packet_list_dissect_and_cache_all(PacketList *packet_list)
792 PacketListRecord *record;
794 int progbar_nextstep;
796 gboolean progbar_stop_flag;
797 GTimeVal progbar_start_time;
799 progdlg_t *progbar = NULL;
800 gchar progbar_status_str[100];
801 gint progbar_loop_max;
802 gint progbar_loop_var;
803 gint progbar_updates = 100 /* 100% */;
805 g_assert(packet_list->columnized == FALSE);
807 progbar_loop_max = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
808 /* Update the progress bar when it gets to this value. */
809 progbar_nextstep = 0;
810 /* When we reach the value that triggers a progress bar update,
811 bump that value by this amount. */
812 progbar_quantum = progbar_loop_max/progbar_updates;
813 /* Progress so far. */
816 progbar_stop_flag = FALSE;
817 g_get_current_time(&progbar_start_time);
819 main_window_update();
821 for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var) {
822 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, progbar_loop_var);
823 packet_list_dissect_and_cache_record(packet_list, record, TRUE, FALSE);
825 /* Create the progress bar if necessary.
826 We check on every iteration of the loop, so that it takes no
827 longer than the standard time to create it (otherwise, for a
828 large file, we might take considerably longer than that standard
829 time in order to get to the next progress bar step). */
831 /* Note: The following may call gtk_main_iteration() which will */
832 /* allow certain "interupts" to happen during this code. */
833 /* (Note that the progress_dlg window is set to "modal" */
834 /* so that clicking on other windows is disabled). */
835 progbar = delayed_create_progress_dlg("Construct", "Columns",
836 TRUE, &progbar_stop_flag,
837 &progbar_start_time, progbar_val);
839 if (progbar_loop_var >= progbar_nextstep) {
840 /* let's not divide by zero. We should never be started
841 * with count == 0, so let's assert that */
842 g_assert(progbar_loop_max > 0);
844 progbar_val = (gfloat) progbar_loop_var / progbar_loop_max;
846 if (progbar != NULL) {
847 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
848 "%u of %u frames", progbar_loop_var+1, progbar_loop_max);
849 /* Note: See comment above re use of gtk_main_iteration() */
850 update_progress_dlg(progbar, progbar_val, progbar_status_str);
853 progbar_nextstep += progbar_quantum;
856 if (progbar_stop_flag) {
857 /* Well, the user decided to abort ... */
862 /* We're done; destroy the progress bar if it was created. */
864 destroy_progress_dlg(progbar);
866 if (progbar_stop_flag) {
867 return FALSE; /* user aborted before columnization completed */
870 packet_list->columnized = TRUE;
874 /* packet_list_do_packet_list_dissect_and_cache_all()
876 * TRUE: if columnization not needed or columnization completed;
877 * FALSE: columnization did not complete (i.e., stopped by the user)
880 packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id)
882 if (!packet_list_column_contains_values(packet_list, sort_col_id)) {
883 return packet_list_dissect_and_cache_all(packet_list);
889 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
893 PacketList *packet_list;
895 g_return_if_fail(sortable != NULL);
896 g_return_if_fail(PACKETLIST_IS_LIST(sortable));
898 packet_list = PACKET_LIST(sortable);
900 if(packet_list->sort_id == sort_col_id &&
901 packet_list->sort_order == order)
904 packet_list->sort_id = sort_col_id;
905 packet_list->sort_order = order;
907 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
910 packet_list_resort(packet_list);
912 /* emit "sort-column-changed" signal to tell any tree views
913 * that the sort column has changed (so the little arrow
914 * in the column header of the sort column is drawn
915 * in the right column) */
917 gtk_tree_sortable_sort_column_changed(sortable);
921 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
922 gint sort_col_id _U_,
923 GtkTreeIterCompareFunc sort_func _U_,
924 gpointer user_data _U_,
925 GDestroyNotify destroy_func _U_)
927 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
931 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
932 GtkTreeIterCompareFunc sort_func _U_,
933 gpointer user_data _U_,
934 GDestroyNotify destroy_func _U_)
936 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
940 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
942 return FALSE; /* Since packet_list_sortable_set_sort_func and
943 set_default_sort_func are not implemented. */
947 packet_list_compare_custom(gint sort_id, PacketListRecord *a, PacketListRecord *b)
949 header_field_info *hfi;
951 hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[sort_id]);
954 return frame_data_compare(a->fdata, b->fdata, COL_NUMBER);
955 } else if ((hfi->strings == NULL) &&
956 (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
957 ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
958 (hfi->display == BASE_OCT))) ||
959 (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
960 (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
961 (hfi->type == FT_RELATIVE_TIME)))
963 /* Attempt to convert to numbers */
964 double num_a = atof(a->col_text[sort_id]);
965 double num_b = atof(b->col_text[sort_id]);
969 else if (num_a > num_b)
972 return frame_data_compare(a->fdata, b->fdata, COL_NUMBER);
975 return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
979 _packet_list_compare_records(gint sort_id, PacketListRecord *a,
982 g_assert(a->col_text);
983 g_assert(b->col_text);
984 g_assert(a->col_text[sort_id]);
985 g_assert(b->col_text[sort_id]);
987 if(a->col_text[sort_id] == b->col_text[sort_id])
988 return 0; /* no need to call strcmp() */
990 if (cfile.cinfo.col_fmt[sort_id] == COL_CUSTOM) {
991 return packet_list_compare_custom (sort_id, a, b);
993 return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
997 packet_list_compare_records(gint sort_id, PacketListRecord *a,
1002 if (col_based_on_frame_data(&cfile.cinfo, sort_id))
1003 return frame_data_compare(a->fdata, b->fdata, cfile.cinfo.col_fmt[sort_id]);
1005 ret = _packet_list_compare_records(sort_id, a, b);
1007 ret = a->fdata->num - b->fdata->num;
1012 packet_list_qsort_physical_compare_func(PacketListRecord **a, PacketListRecord **b,
1013 PacketList *packet_list)
1017 g_assert((a) && (b) && (packet_list));
1019 ret = packet_list_compare_records(packet_list->sort_id, *a, *b);
1021 /* Swap -1 and 1 if sort order is reverse */
1022 if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
1023 ret = (ret < 0) ? 1 : -1;
1029 packet_list_resort(PacketList *packet_list)
1031 PacketListRecord *record;
1037 g_return_if_fail(packet_list != NULL);
1038 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1040 if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1043 /* resort physical rows according to sorting column */
1044 g_ptr_array_sort_with_data(packet_list->physical_rows,
1045 (GCompareDataFunc) packet_list_qsort_physical_compare_func,
1048 /* let other objects know about the new order */
1049 neworder = g_new0(gint, PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1051 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1052 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1053 record->physical_pos = phy_idx;
1054 g_assert(record->visible_pos >= -1);
1055 if (record->visible_pos >= 0) {
1056 g_assert(record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
1057 neworder[vis_idx] = record->visible_pos;
1058 PACKET_LIST_RECORD_SET(packet_list->visible_rows, vis_idx, record);
1059 record->visible_pos = vis_idx;
1064 g_assert(vis_idx == PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1066 path = gtk_tree_path_new();
1068 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
1071 gtk_tree_path_free(path);
1076 packet_list_recreate_visible_rows(PacketList *packet_list)
1080 PacketListRecord *record;
1082 g_return_val_if_fail(packet_list != NULL, 0);
1083 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), 0);
1085 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
1088 if(packet_list->visible_rows)
1089 g_ptr_array_free(packet_list->visible_rows, TRUE);
1091 packet_list->visible_rows = g_ptr_array_new();
1093 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1094 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1095 if (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time) {
1096 record->visible_pos = vis_idx++;
1097 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, record);
1100 record->visible_pos = -1;
1107 packet_list_dissect_and_cache_iter(PacketList *packet_list, GtkTreeIter *iter, gboolean dissect_columns, gboolean dissect_color)
1109 PacketListRecord *record;
1111 g_return_if_fail(packet_list != NULL);
1112 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1113 g_return_if_fail(iter != NULL);
1114 g_return_if_fail(iter->user_data != NULL);
1116 record = iter->user_data;
1118 packet_list_dissect_and_cache_record(packet_list, record, dissect_columns, dissect_color);
1122 packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_columns, gboolean dissect_color)
1128 gboolean create_proto_tree;
1129 union wtap_pseudo_header pseudo_header; /* Packet pseudo_header */
1130 guint8 pd[WTAP_MAX_PACKET_SIZE]; /* Packet data */
1132 /* XXX: Does it work to check if the record is already columnized/colorized ?
1133 * i.e.: test record->columnized and record->colorized and just return
1134 * if they're both TRUE.
1135 * Note: Part of the patch submitted with Bug #4273 had code to do this but it
1136 * was commented out in the patch and was not included in SVN #33011.
1138 fdata = record->fdata;
1140 if (dissect_columns)
1141 cinfo = &cfile.cinfo;
1145 if (!cf_read_frame_r(&cfile, fdata, &pseudo_header, pd)) {
1147 * Error reading the frame.
1149 * Don't set the color filter for now (we might want
1150 * to colorize it in some fashion to warn that the
1151 * row couldn't be filled in or colorized), and
1152 * set the columns to placeholder values, except
1153 * for the Info column, where we'll put in an
1156 if (dissect_columns) {
1157 col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
1159 for(col = 0; col < cinfo->num_cols; ++col) {
1160 /* Skip columns based on frame_data because we already store those. */
1161 if (!col_based_on_frame_data(cinfo, col))
1162 packet_list_change_record(packet_list, record->physical_pos, col, cinfo);
1164 record->columnized = TRUE;
1166 if (dissect_color) {
1167 fdata->color_filter = NULL;
1168 record->colorized = TRUE;
1170 return; /* error reading the frame */
1173 create_proto_tree = (color_filters_used() && dissect_color) ||
1174 (have_custom_cols(cinfo) && dissect_columns);
1176 epan_dissect_init(&edt,
1178 FALSE /* proto_tree_visible */);
1181 color_filters_prime_edt(&edt);
1182 if (dissect_columns)
1183 col_custom_prime_edt(&edt, cinfo);
1185 epan_dissect_run(&edt, &pseudo_header, pd, fdata, cinfo);
1188 fdata->color_filter = color_filters_colorize_packet(&edt);
1190 if (dissect_columns) {
1191 /* "Stringify" non frame_data vals */
1192 epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */);
1194 for(col = 0; col < cinfo->num_cols; ++col) {
1195 /* Skip columns based on frame_data because we already store those. */
1196 if (!col_based_on_frame_data(cinfo, col))
1197 packet_list_change_record(packet_list, record->physical_pos, col, cinfo);
1201 if (dissect_columns)
1202 record->columnized = TRUE;
1204 record->colorized = TRUE;
1206 epan_dissect_cleanup(&edt);
1210 packet_list_reset_colorized(PacketList *packet_list)
1212 PacketListRecord *record;
1215 for(i = 0; i < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++i) {
1216 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, i);
1217 record->colorized = FALSE;
1222 packet_list_get_widest_column_string(PacketList *packet_list, gint col)
1224 g_return_val_if_fail(packet_list != NULL, NULL);
1225 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), NULL);
1226 g_return_val_if_fail(col < packet_list->n_columns && col >= 0, NULL);
1228 if (PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1231 if (col_based_on_frame_data(&cfile.cinfo, col)) {
1232 PacketListRecord *record;
1236 memset (&fdata, 0, sizeof fdata);
1238 nstime_set_zero(&fdata.abs_ts);
1239 nstime_set_zero(&fdata.rel_ts);
1240 nstime_set_zero(&fdata.del_cap_ts);
1241 nstime_set_zero(&fdata.del_dis_ts);
1243 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1244 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1245 switch (cfile.cinfo.col_fmt[col]) {
1248 if (record->fdata->num > fdata.num)
1249 fdata.num = record->fdata->num;
1251 case COL_PACKET_LENGTH:
1252 if (record->fdata->pkt_len > fdata.pkt_len)
1253 fdata.pkt_len = record->fdata->pkt_len;
1255 case COL_CUMULATIVE_BYTES:
1256 if (record->fdata->cum_bytes > fdata.cum_bytes)
1257 fdata.cum_bytes = record->fdata->cum_bytes;
1260 case COL_ABS_DATE_TIME:
1262 case COL_UTC_DATE_TIME:
1263 if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
1264 fdata.abs_ts = record->fdata->abs_ts;
1267 if (nstime_cmp(&record->fdata->rel_ts, &fdata.rel_ts) > 0)
1268 fdata.rel_ts = record->fdata->rel_ts;
1270 case COL_DELTA_TIME:
1271 if (nstime_cmp(&record->fdata->del_cap_ts, &fdata.del_cap_ts) > 0)
1272 fdata.del_cap_ts = record->fdata->del_cap_ts;
1274 case COL_DELTA_TIME_DIS:
1275 if (nstime_cmp(&record->fdata->del_dis_ts, &fdata.del_dis_ts) > 0)
1276 fdata.del_dis_ts = record->fdata->del_dis_ts;
1279 switch (timestamp_get_type()) {
1281 case TS_ABSOLUTE_WITH_DATE:
1283 case TS_UTC_WITH_DATE:
1284 if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
1285 fdata.abs_ts = record->fdata->abs_ts;
1289 if (nstime_cmp(&record->fdata->rel_ts, &fdata.rel_ts) > 0)
1290 fdata.rel_ts = record->fdata->rel_ts;
1294 if (nstime_cmp(&record->fdata->del_cap_ts, &fdata.del_cap_ts) > 0)
1295 fdata.del_cap_ts = record->fdata->del_cap_ts;
1299 if (nstime_cmp(&record->fdata->del_dis_ts, &fdata.del_dis_ts) > 0)
1300 fdata.del_dis_ts = record->fdata->del_dis_ts;
1304 if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
1305 fdata.abs_ts = record->fdata->abs_ts;
1309 /* code is missing for this case, but I don't know which [jmayer20051219] */
1310 g_assert_not_reached();
1316 g_assert_not_reached();
1320 col_fill_in_frame_data(&fdata, &cfile.cinfo, col, FALSE);
1322 return cfile.cinfo.col_buf[col];
1325 PacketListRecord *record;
1328 gchar *widest_column_str = NULL;
1329 guint widest_column_len = 0;
1331 if (!packet_list->columnized)
1332 packet_list_dissect_and_cache_all(packet_list); /* XXX: need to handle case of "incomplete" ? */
1334 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1335 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1336 if (record->col_text_len[col] > widest_column_len) {
1337 widest_column_str = record->col_text[col];
1338 widest_column_len = record->col_text_len[col];
1342 return widest_column_str;