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 */
38 #include "packet_list_store.h"
40 #include "ui/progress_dlg.h"
41 #include "ui/ui_util.h"
43 #include "ui/gtk/old-gtk-compat.h"
45 #include <epan/epan_dissect.h>
46 #include <epan/column_info.h>
47 #include <epan/column.h>
50 #include "color_filters.h"
51 #include "frame_tvbuff.h"
55 /* #define PACKET_PARANOID_CHECKS */
57 /** PacketListRecord: represents a row */
58 typedef struct _PacketListRecord {
59 /** The column text for some columns */
60 const gchar **col_text;
61 /**< The length of the column text strings in 'col_text' */
62 gushort *col_text_len;
66 /* admin stuff used by the custom list model */
67 #ifdef PACKET_PARANOID_CHECKS
68 /** position within the physical array */
71 /** position within the visible array */
74 /** Has this record been colorized? */
79 static void packet_list_init(PacketList *pkg_tree);
80 static void packet_list_class_init(PacketListClass *klass);
81 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
82 static void packet_list_finalize(GObject *object);
83 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
84 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
85 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint idx);
86 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
87 GtkTreeIter *iter, GtkTreePath *path);
88 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
90 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
91 gint column, GValue *value);
92 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
94 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
97 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
98 GtkTreeIter *iter _U_);
99 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
101 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
105 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
106 GtkTreeIter *iter _U_,
107 GtkTreeIter *child _U_);
109 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
113 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
116 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
118 GtkTreeIterCompareFunc sort_func,
120 GDestroyNotify destroy_func);
121 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
123 GtkTreeIterCompareFunc
128 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
130 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
131 static void packet_list_resort(PacketList *packet_list);
132 static void packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color );
134 static GObjectClass *parent_class = NULL;
138 packet_list_get_type(void)
140 static GType packet_list_type = 0;
142 if(packet_list_type == 0) {
143 static const GTypeInfo packet_list_info = {
144 sizeof(PacketListClass),
145 NULL, /* base_init */
146 NULL, /* base_finalize */
147 (GClassInitFunc) packet_list_class_init,
148 NULL, /* class finalize */
149 NULL, /* class_data */
152 (GInstanceInitFunc) packet_list_init,
153 NULL /* value_table */
156 static const GInterfaceInfo tree_model_info = {
157 (GInterfaceInitFunc) packet_list_tree_model_init,
162 static const GInterfaceInfo tree_sortable_info = {
163 (GInterfaceInitFunc) packet_list_sortable_init,
168 /* Register the new derived type with the GObject type system */
169 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
174 g_type_add_interface_static(packet_list_type,
179 /* Register our GtkTreeModel interface with the type system */
180 g_type_add_interface_static(packet_list_type,
181 GTK_TYPE_TREE_SORTABLE,
182 &tree_sortable_info);
185 return packet_list_type;
189 packet_list_sortable_init(GtkTreeSortableIface *iface)
191 iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
192 iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
193 /* The following three functions are not implemented */
194 iface->set_sort_func = packet_list_sortable_set_sort_func;
195 iface->set_default_sort_func =
196 packet_list_sortable_set_default_sort_func;
197 iface->has_default_sort_func =
198 packet_list_sortable_has_default_sort_func;
202 packet_list_class_init(PacketListClass *klass)
204 GObjectClass *object_class;
206 parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
207 object_class = (GObjectClass*) klass;
209 object_class->finalize = packet_list_finalize;
211 #if !GTK_CHECK_VERSION(3,0,0)
212 /* XXX this seems to affect TreeView Application wide
213 * Move to main.c ??? as it's not a bad thing(tm)
215 gtk_rc_parse_string (
216 "style \"PacketList-style\"\n"
218 " GtkTreeView::horizontal-separator = 0\n"
219 " GtkTreeView::vertical-separator = 1\n"
220 "} widget_class \"*TreeView*\""
221 " style \"PacketList-style\"");
227 packet_list_tree_model_init(GtkTreeModelIface *iface)
229 iface->get_flags = packet_list_get_flags;
230 iface->get_n_columns = packet_list_get_n_columns;
231 iface->get_column_type = packet_list_get_column_type;
232 iface->get_iter = packet_list_get_iter;
233 iface->get_path = packet_list_get_path;
234 iface->get_value = packet_list_get_value;
235 iface->iter_next = packet_list_iter_next;
236 iface->iter_children = packet_list_iter_children;
237 iface->iter_has_child = packet_list_iter_has_child;
238 iface->iter_n_children = packet_list_iter_n_children;
239 iface->iter_nth_child = packet_list_iter_nth_child;
240 iface->iter_parent = packet_list_iter_parent;
243 /* This is called every time a new packet list object instance is created in
244 * packet_list_new. Initialize the list structure's fields here. */
246 packet_list_init(PacketList *packet_list)
250 /* To check whether an iter belongs to our model. */
251 packet_list->stamp = g_random_int();
253 packet_list->n_cols = cfile.cinfo.num_cols;
254 packet_list->physical_rows = g_ptr_array_new();
255 packet_list->visible_rows = g_ptr_array_new();
257 packet_list->columnized = FALSE;
258 packet_list->sort_id = 0; /* defaults to first column for now */
259 packet_list->sort_order = GTK_SORT_ASCENDING;
261 packet_list->col_to_text = g_new(int, packet_list->n_cols);
262 for (i = 0, j = 0; i < packet_list->n_cols; i++) {
263 if (!col_based_on_frame_data(&cfile.cinfo, i)) {
264 packet_list->col_to_text[i] = j;
267 packet_list->col_to_text[i] = -1;
269 packet_list->n_text_cols = j;
271 #ifdef PACKET_LIST_STATISTICS
272 packet_list->const_strings = 0;
276 /* This function is called just before a packet list is destroyed. Free
277 * dynamically allocated memory here. */
279 packet_list_finalize(GObject *object)
281 /* PacketList *packet_list = PACKET_LIST(object); */
283 /* XXX - Free all records and free all memory used by the list */
285 /* must chain up - finalize parent */
286 (* parent_class->finalize) (object);
289 static GtkTreeModelFlags
290 packet_list_get_flags(GtkTreeModel *tree_model)
292 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
293 (GtkTreeModelFlags)0);
295 return (GtkTreeModelFlags)(GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
299 packet_list_get_n_columns(GtkTreeModel *tree_model)
301 PacketList *packet_list;
303 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
304 packet_list = (PacketList *) tree_model;
306 /* Note: We need one extra column to store the entire frame_data */
307 return packet_list->n_cols + 1;
311 packet_list_get_column_type(GtkTreeModel *tree_model, gint idx)
313 PacketList *packet_list;
315 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
316 packet_list = (PacketList *) tree_model;
318 /* Note: We use one extra column to store the entire frame_data */
319 g_return_val_if_fail(idx >= 0 && idx < packet_list->n_cols + 1, G_TYPE_INVALID);
321 if (idx >= 0 && idx < packet_list->n_cols)
322 return G_TYPE_STRING;
323 else if (idx == packet_list->n_cols)
324 return G_TYPE_POINTER;
326 return G_TYPE_INVALID;
330 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
333 gint *indices, depth;
335 g_assert(PACKETLIST_IS_LIST(tree_model));
337 g_assert(path != NULL);
339 indices = gtk_tree_path_get_indices(path);
340 depth = gtk_tree_path_get_depth(path);
342 /* we do not allow children since it's just a list */
343 g_assert(depth == 1);
345 return packet_list_iter_nth_child(tree_model, iter, NULL, indices[0]);
349 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
352 PacketListRecord *record;
353 PacketList *packet_list;
355 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
356 packet_list = (PacketList *) tree_model;
358 g_return_val_if_fail(iter != NULL, NULL);
359 g_return_val_if_fail(iter->stamp == packet_list->stamp, NULL);
360 g_return_val_if_fail(iter->user_data != NULL, NULL);
362 record = (PacketListRecord*) iter->user_data;
364 path = gtk_tree_path_new();
365 gtk_tree_path_append_index(path, record->visible_pos);
371 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
374 PacketListRecord *record;
375 PacketList *packet_list;
377 g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
378 packet_list = (PacketList *) tree_model;
380 g_return_if_fail(iter != NULL);
381 g_return_if_fail(iter->stamp == packet_list->stamp);
382 g_return_if_fail(iter->user_data != NULL);
384 /* Note: We use one extra column to store the entire frame_data */
385 g_return_if_fail(column >= 0 && column < packet_list->n_cols + 1);
387 record = (PacketListRecord*) iter->user_data;
389 #ifdef PACKET_PARANOID_CHECKS
390 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->physical_rows, record->physical_pos));
392 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, record->visible_pos));
394 if (column >= 0 && column < packet_list->n_cols) {
397 g_value_init(value, G_TYPE_STRING);
399 if (record->col_text == NULL || !record->colorized)
400 packet_list_dissect_and_cache_record(packet_list, record, !record->colorized);
402 text_column = packet_list->col_to_text[column];
403 if (text_column == -1) { /* column based on frame_data */
404 col_fill_in_frame_data(record->fdata, &cfile.cinfo, column, FALSE);
405 g_value_set_string(value, cfile.cinfo.col_data[column]);
407 g_return_if_fail(record->col_text);
408 g_value_set_string(value, record->col_text[text_column]);
411 } else if (column == packet_list->n_cols) {
412 g_value_init(value, G_TYPE_POINTER);
413 g_value_set_pointer(value, record->fdata);
417 static PacketListRecord *
418 packet_list_iter_next_visible(PacketList *packet_list, PacketListRecord *record)
420 PacketListRecord *nextrecord;
421 gint next_visible_pos;
423 g_assert(record->visible_pos >= 0);
424 next_visible_pos = record->visible_pos + 1;
426 /* Is this the last record in the list? */
427 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, next_visible_pos))
430 nextrecord = PACKET_LIST_RECORD_GET(packet_list->visible_rows, next_visible_pos);
432 g_assert(nextrecord->visible_pos == (record->visible_pos + 1));
433 #ifdef PACKET_PARANOID_CHECKS
434 g_assert(nextrecord->physical_pos >= (record->physical_pos + 1));
440 /* Takes an iter structure and sets it to point to the next row. */
442 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
444 PacketListRecord *record, *nextrecord;
445 PacketList *packet_list;
447 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
448 packet_list = (PacketList *) tree_model;
453 g_return_val_if_fail(iter->stamp == packet_list->stamp, FALSE);
454 g_return_val_if_fail(iter->user_data, FALSE);
456 record = (PacketListRecord*) iter->user_data;
457 nextrecord = packet_list_iter_next_visible(packet_list, record);
462 /* iter->stamp = packet_list->stamp; */
463 iter->user_data = nextrecord;
469 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
472 return packet_list_iter_nth_child(tree_model, iter, parent, 0);
476 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
478 return FALSE; /* Lists have no children */
482 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
484 PacketList *packet_list;
486 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
487 packet_list = (PacketList *) tree_model;
490 /* special case: if iter == NULL, return number of top-level rows */
491 return PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
494 g_return_val_if_fail(iter->stamp == packet_list->stamp, 0);
495 g_return_val_if_fail(iter->user_data, 0);
496 /* Lists have zero children */
502 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
503 GtkTreeIter *parent, gint n)
505 PacketListRecord *record;
506 PacketList *packet_list;
508 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
509 packet_list = (PacketList *) tree_model;
511 /* A list only has top-level rows */
513 g_return_val_if_fail(parent->stamp == packet_list->stamp, FALSE);
514 g_return_val_if_fail(parent->user_data, FALSE);
518 /* Special case: if parent == NULL, set iter to n-th top-level row. */
519 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
522 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
524 g_assert(record->visible_pos == n);
526 iter->stamp = packet_list->stamp;
527 iter->user_data = record;
533 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
534 GtkTreeIter *child _U_)
536 return FALSE; /* No parents since no children in a list */
540 packet_list_new(void)
542 PacketList *newpacketlist;
544 newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
546 g_assert(newpacketlist != NULL);
548 return newpacketlist;
553 packet_list_row_deleted(PacketList *packet_list, guint pos)
557 /* Inform the tree view and other interested objects (such as tree row
558 * references) that we have deleted a row */
559 path = gtk_tree_path_new();
560 gtk_tree_path_append_index(path, pos);
562 gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
564 gtk_tree_path_free(path);
569 packet_list_store_clear(PacketList *packet_list)
571 g_return_if_fail(packet_list != NULL);
572 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
574 /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
575 * the model from the view.
576 for( ; packet_list->num_rows > 0; --packet_list->num_rows)
577 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
580 /* XXX - hold on to these rows and reuse them instead */
581 if(packet_list->physical_rows)
582 g_ptr_array_free(packet_list->physical_rows, TRUE);
583 if(packet_list->visible_rows)
584 g_ptr_array_free(packet_list->visible_rows, TRUE);
585 packet_list->physical_rows = g_ptr_array_new();
586 packet_list->visible_rows = g_ptr_array_new();
588 packet_list->columnized = FALSE;
590 /* Generate new number */
591 packet_list->stamp = g_random_int();
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 = se_new(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;
654 packet_list_change_record(PacketList *packet_list, PacketListRecord *record, gint col, column_info *cinfo)
660 text_col = packet_list->col_to_text[col];
662 /* Column based on frame_data or it already contains a value */
663 if (text_col == -1 || record->col_text[text_col] != NULL)
666 switch (cfile.cinfo.col_fmt[col]) {
668 case COL_RES_SRC: /* COL_DEF_SRC is currently just like COL_RES_SRC */
672 case COL_UNRES_DL_SRC:
673 case COL_DEF_NET_SRC:
674 case COL_RES_NET_SRC:
675 case COL_UNRES_NET_SRC:
677 case COL_RES_DST: /* COL_DEF_DST is currently just like COL_RES_DST */
681 case COL_UNRES_DL_DST:
682 case COL_DEF_NET_DST:
683 case COL_RES_NET_DST:
684 case COL_UNRES_NET_DST:
689 case COL_8021Q_VLAN_ID:
692 if (cinfo->col_data[col] && cinfo->col_data[col] != cinfo->col_buf[col]) {
693 col_text_len = strlen(cinfo->col_data[col]);
694 if (col_text_len > G_MAXUSHORT)
695 col_text_len = G_MAXUSHORT;
697 /* This is a constant string, so we don't have to copy it */
698 record->col_text[text_col] = (gchar *) cinfo->col_data[col];
699 record->col_text_len[text_col] = (gushort) col_text_len;
700 #ifdef PACKET_LIST_STATISTICS
701 ++packet_list->const_strings;
705 /* !! FALL-THROUGH!! */
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;
713 if (!record->col_text_len[text_col]) {
714 record->col_text[text_col] = "";
715 #ifdef PACKET_LIST_STATISTICS
716 ++packet_list->const_strings;
721 if(!packet_list->string_pool)
722 packet_list->string_pool = g_string_chunk_new(32);
723 if (!get_column_resolved (col) && cinfo->col_expr.col_expr_val[col]) {
724 /* Use the unresolved value in col_expr_val */
725 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_expr.col_expr_val[col]);
727 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_data[col]);
729 record->col_text[text_col] = str;
735 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
739 PacketList *packet_list;
741 g_return_val_if_fail(sortable != NULL, FALSE);
742 g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
744 packet_list = (PacketList *) sortable;
747 *sort_col_id = packet_list->sort_id;
750 *order = packet_list->sort_order;
756 packet_list_column_contains_values(PacketList *packet_list, gint sort_col_id)
758 if (packet_list->columnized || col_based_on_frame_data(&cfile.cinfo, sort_col_id))
764 /* packet_list_dissect_and_cache_all()
766 * TRUE if columnization completed;
767 * packet_list->columnized set to TRUE;
768 * FALSE: columnization did not complete (i.e., was stopped by the user);
769 * packet_list->columnized unchanged (i.e., FALSE).
773 packet_list_dissect_and_cache_all(PacketList *packet_list)
775 PacketListRecord *record;
777 int progbar_nextstep;
779 gboolean progbar_stop_flag;
780 GTimeVal progbar_start_time;
782 progdlg_t *progbar = NULL;
783 gchar progbar_status_str[100];
784 gint progbar_loop_max;
785 gint progbar_loop_var;
786 gint progbar_updates = 100 /* 100% */;
788 g_assert(packet_list->columnized == FALSE);
790 progbar_loop_max = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
791 /* Update the progress bar when it gets to this value. */
792 progbar_nextstep = 0;
793 /* When we reach the value that triggers a progress bar update,
794 bump that value by this amount. */
795 progbar_quantum = progbar_loop_max/progbar_updates;
796 /* Progress so far. */
799 progbar_stop_flag = FALSE;
800 g_get_current_time(&progbar_start_time);
802 main_window_update();
804 for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var) {
805 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, progbar_loop_var);
806 packet_list_dissect_and_cache_record(packet_list, record, FALSE);
808 /* Create the progress bar if necessary.
809 We check on every iteration of the loop, so that it takes no
810 longer than the standard time to create it (otherwise, for a
811 large file, we might take considerably longer than that standard
812 time in order to get to the next progress bar step). */
814 /* Note: The following may call gtk_main_iteration() which will */
815 /* allow certain "interupts" to happen during this code. */
816 /* (Note that the progress_dlg window is set to "modal" */
817 /* so that clicking on other windows is disabled). */
818 progbar = delayed_create_progress_dlg(gtk_widget_get_window(packet_list->view),
819 "Construct", "Columns",
820 TRUE, &progbar_stop_flag,
821 &progbar_start_time, progbar_val);
823 if (progbar_loop_var >= progbar_nextstep) {
824 /* let's not divide by zero. We should never be started
825 * with count == 0, so let's assert that */
826 g_assert(progbar_loop_max > 0);
828 progbar_val = (gfloat) progbar_loop_var / progbar_loop_max;
830 if (progbar != NULL) {
831 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
832 "%u of %u frames", progbar_loop_var+1, progbar_loop_max);
833 /* Note: See comment above re use of gtk_main_iteration() */
834 update_progress_dlg(progbar, progbar_val, progbar_status_str);
837 progbar_nextstep += progbar_quantum;
840 if (progbar_stop_flag) {
841 /* Well, the user decided to abort ... */
846 /* We're done; destroy the progress bar if it was created. */
848 destroy_progress_dlg(progbar);
850 if (progbar_stop_flag) {
851 return FALSE; /* user aborted before columnization completed */
854 packet_list->columnized = TRUE;
858 /* packet_list_do_packet_list_dissect_and_cache_all()
860 * TRUE: if columnization not needed or columnization completed;
861 * FALSE: columnization did not complete (i.e., stopped by the user)
864 packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id)
866 if (!packet_list_column_contains_values(packet_list, sort_col_id)) {
867 return packet_list_dissect_and_cache_all(packet_list);
873 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
877 PacketList *packet_list;
879 g_return_if_fail(sortable != NULL);
880 g_return_if_fail(PACKETLIST_IS_LIST(sortable));
882 packet_list = (PacketList *) sortable;
884 if(packet_list->sort_id == sort_col_id &&
885 packet_list->sort_order == order)
888 packet_list->sort_id = sort_col_id;
889 packet_list->sort_order = order;
891 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
894 packet_list_resort(packet_list);
896 /* emit "sort-column-changed" signal to tell any tree views
897 * that the sort column has changed (so the little arrow
898 * in the column header of the sort column is drawn
899 * in the right column) */
901 gtk_tree_sortable_sort_column_changed(sortable);
905 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
906 gint sort_col_id _U_,
907 GtkTreeIterCompareFunc sort_func _U_,
908 gpointer user_data _U_,
909 GDestroyNotify destroy_func _U_)
911 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
915 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
916 GtkTreeIterCompareFunc sort_func _U_,
917 gpointer user_data _U_,
918 GDestroyNotify destroy_func _U_)
920 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
924 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
926 return FALSE; /* Since packet_list_sortable_set_sort_func and
927 set_default_sort_func are not implemented. */
931 packet_list_compare_custom(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
933 header_field_info *hfi;
935 hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[sort_id]);
938 return frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
939 } else if ((hfi->strings == NULL) &&
940 (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
941 ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
942 (hfi->display == BASE_OCT))) ||
943 (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
944 (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
945 (hfi->type == FT_RELATIVE_TIME)))
947 /* Attempt to convert to numbers */
948 double num_a = atof(a->col_text[text_sort_id]);
949 double num_b = atof(b->col_text[text_sort_id]);
953 else if (num_a > num_b)
959 return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
963 _packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
965 g_assert(a->col_text);
966 g_assert(b->col_text);
967 g_assert(a->col_text[text_sort_id]);
968 g_assert(b->col_text[text_sort_id]);
970 if(a->col_text[text_sort_id] == b->col_text[text_sort_id])
971 return 0; /* no need to call strcmp() */
973 if (cfile.cinfo.col_fmt[sort_id] == COL_CUSTOM)
974 return packet_list_compare_custom(sort_id, text_sort_id, a, b);
976 return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
980 packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
984 if (text_sort_id == -1) /* based on frame_data ? */
985 return frame_data_compare(cfile.epan, a->fdata, b->fdata, cfile.cinfo.col_fmt[sort_id]);
987 ret = _packet_list_compare_records(sort_id, text_sort_id, a, b);
989 ret = frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
994 packet_list_qsort_physical_compare_func(PacketListRecord **a, PacketListRecord **b,
995 PacketList *packet_list)
998 gint sort_id = packet_list->sort_id;
1000 g_assert((a) && (b) && (packet_list));
1002 ret = packet_list_compare_records(sort_id, packet_list->col_to_text[sort_id], *a, *b);
1004 /* Swap -1 and 1 if sort order is reverse */
1005 if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
1006 ret = (ret < 0) ? 1 : -1;
1012 packet_list_resort(PacketList *packet_list)
1014 PacketListRecord *record;
1020 g_return_if_fail(packet_list != NULL);
1021 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1023 if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1026 /* resort physical rows according to sorting column */
1027 g_ptr_array_sort_with_data(packet_list->physical_rows,
1028 (GCompareDataFunc) packet_list_qsort_physical_compare_func,
1031 /* let other objects know about the new order */
1032 neworder = g_new0(gint, PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1034 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1035 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1036 #ifdef PACKET_PARANOID_CHECKS
1037 record->physical_pos = phy_idx;
1039 g_assert(record->visible_pos >= -1);
1040 if (record->visible_pos >= 0) {
1041 g_assert(record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
1042 neworder[vis_idx] = record->visible_pos;
1043 PACKET_LIST_RECORD_SET(packet_list->visible_rows, vis_idx, record);
1044 record->visible_pos = vis_idx;
1049 g_assert(vis_idx == PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1051 path = gtk_tree_path_new();
1053 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
1056 gtk_tree_path_free(path);
1061 packet_list_recreate_visible_rows_list(PacketList *packet_list)
1065 PacketListRecord *record;
1067 g_return_val_if_fail(packet_list != NULL, 0);
1068 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), 0);
1070 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
1073 if(packet_list->visible_rows)
1074 g_ptr_array_free(packet_list->visible_rows, TRUE);
1076 packet_list->visible_rows = g_ptr_array_new();
1078 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1079 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1080 if (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time) {
1081 record->visible_pos = vis_idx++;
1082 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, record);
1085 record->visible_pos = -1;
1092 packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color)
1098 gboolean create_proto_tree;
1099 struct wtap_pkthdr phdr; /* Packet header */
1100 Buffer buf; /* Packet data */
1101 gboolean dissect_columns = (record->col_text == NULL);
1103 g_return_if_fail(packet_list);
1104 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1106 fdata = record->fdata;
1108 if (dissect_columns) {
1109 cinfo = &cfile.cinfo;
1111 record->col_text = (const gchar **)se_alloc0(sizeof(*record->col_text) * packet_list->n_text_cols);
1112 record->col_text_len = (gushort *)se_alloc0(sizeof(*record->col_text_len) * packet_list->n_text_cols);
1116 buffer_init(&buf, 1500);
1117 if (!cf_read_frame_r(&cfile, fdata, &phdr, &buf)) {
1119 * Error reading the frame.
1121 * Don't set the color filter for now (we might want
1122 * to colorize it in some fashion to warn that the
1123 * row couldn't be filled in or colorized), and
1124 * set the columns to placeholder values, except
1125 * for the Info column, where we'll put in an
1128 if (dissect_columns) {
1129 col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
1131 for(col = 0; col < cinfo->num_cols; ++col)
1132 packet_list_change_record(packet_list, record, col, cinfo);
1134 if (dissect_color) {
1135 fdata->color_filter = NULL;
1136 record->colorized = TRUE;
1139 return; /* error reading the frame */
1142 create_proto_tree = (dissect_color && color_filters_used()) ||
1143 (dissect_columns && have_custom_cols(cinfo));
1145 epan_dissect_init(&edt, cfile.epan,
1147 FALSE /* proto_tree_visible */);
1150 color_filters_prime_edt(&edt);
1151 if (dissect_columns)
1152 col_custom_prime_edt(&edt, cinfo);
1155 * XXX - need to catch an OutOfMemoryError exception and
1156 * attempt to recover from it.
1158 epan_dissect_run(&edt, &phdr, frame_tvbuff_new_buffer(fdata, &buf), fdata, cinfo);
1161 fdata->color_filter = color_filters_colorize_packet(&edt);
1163 if (dissect_columns) {
1164 /* "Stringify" non frame_data vals */
1165 epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */);
1167 for(col = 0; col < cinfo->num_cols; ++col)
1168 packet_list_change_record(packet_list, record, col, cinfo);
1172 record->colorized = TRUE;
1174 epan_dissect_cleanup(&edt);
1179 packet_list_reset_colorized(PacketList *packet_list)
1181 PacketListRecord *record;
1184 for(i = 0; i < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++i) {
1185 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, i);
1186 record->colorized = FALSE;
1191 packet_list_get_widest_column_string(PacketList *packet_list, gint col)
1195 g_return_val_if_fail(packet_list != NULL, NULL);
1196 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), NULL);
1197 /* We need real column here, so not packet_list->n_cols+1 */
1198 g_return_val_if_fail(col >= 0 && col < packet_list->n_cols, NULL);
1200 if (PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1203 text_col = packet_list->col_to_text[col];
1205 if (text_col == -1) { /* column based on frame data */
1206 PacketListRecord *record;
1209 guint widest_packet = 0;
1210 gint widest_column_len = -1;
1212 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1215 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1217 col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1218 column_len = (gint) strlen(cfile.cinfo.col_buf[col]);
1219 if (column_len > widest_column_len) {
1220 widest_column_len = column_len;
1221 widest_packet = vis_idx;
1225 if (widest_column_len != -1) {
1226 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, widest_packet);
1227 col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1229 return cfile.cinfo.col_buf[col];
1234 PacketListRecord *record;
1237 const gchar *widest_column_str = NULL;
1238 guint widest_column_len = 0;
1240 if (!packet_list->columnized)
1241 packet_list_dissect_and_cache_all(packet_list); /* XXX: need to handle case of "incomplete" ? */
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 if (record->col_text_len[text_col] > widest_column_len) {
1246 widest_column_str = record->col_text[text_col];
1247 widest_column_len = record->col_text_len[text_col];
1251 return widest_column_str;