2 * Routines to implement a custom GTK+ list model for Wireshark's packet list
3 * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
4 * * Co-authors Anders Broman and Kovarththanan Rajaratnam.
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
26 /* This code was originally based on the GTK+ Tree View tutorial at
27 * http://scentric.net/tutorial */
36 #include "packet_list_store.h"
38 #include "ui/progress_dlg.h"
39 #include "ui/ui_util.h"
42 #include <epan/epan_dissect.h>
43 #include <epan/column.h>
46 #include "color_filters.h"
47 #include "frame_tvbuff.h"
51 /* #define PACKET_PARANOID_CHECKS */
53 /** PacketListRecord: represents a row */
54 typedef struct _PacketListRecord {
55 /** The column text for some columns */
56 const gchar **col_text;
57 /**< The length of the column text strings in 'col_text' */
58 gushort *col_text_len;
62 /* admin stuff used by the custom list model */
63 #ifdef PACKET_PARANOID_CHECKS
64 /** position within the physical array */
67 /** position within the visible array */
70 /** Has this record been colorized? */
75 static void packet_list_init(PacketList *pkg_tree);
76 static void packet_list_class_init(PacketListClass *klass);
77 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
78 static void packet_list_finalize(GObject *object);
79 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
80 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
81 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint idx);
82 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
83 GtkTreeIter *iter, GtkTreePath *path);
84 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
86 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
87 gint column, GValue *value);
88 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
90 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
93 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
94 GtkTreeIter *iter _U_);
95 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
97 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
101 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
102 GtkTreeIter *iter _U_,
103 GtkTreeIter *child _U_);
105 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
109 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
112 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
114 GtkTreeIterCompareFunc sort_func,
116 GDestroyNotify destroy_func);
117 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
119 GtkTreeIterCompareFunc
124 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
126 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
127 static void packet_list_resort(PacketList *packet_list);
128 static void packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color );
130 static GObjectClass *parent_class = NULL;
134 packet_list_get_type(void)
136 static GType packet_list_type = 0;
138 if(packet_list_type == 0) {
139 static const GTypeInfo packet_list_info = {
140 sizeof(PacketListClass),
141 NULL, /* base_init */
142 NULL, /* base_finalize */
143 (GClassInitFunc) packet_list_class_init,
144 NULL, /* class finalize */
145 NULL, /* class_data */
148 (GInstanceInitFunc) packet_list_init,
149 NULL /* value_table */
152 static const GInterfaceInfo tree_model_info = {
153 (GInterfaceInitFunc) packet_list_tree_model_init,
158 static const GInterfaceInfo tree_sortable_info = {
159 (GInterfaceInitFunc) packet_list_sortable_init,
164 /* Register the new derived type with the GObject type system */
165 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
170 g_type_add_interface_static(packet_list_type,
175 /* Register our GtkTreeModel interface with the type system */
176 g_type_add_interface_static(packet_list_type,
177 GTK_TYPE_TREE_SORTABLE,
178 &tree_sortable_info);
181 return packet_list_type;
185 packet_list_sortable_init(GtkTreeSortableIface *iface)
187 iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
188 iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
189 /* The following three functions are not implemented */
190 iface->set_sort_func = packet_list_sortable_set_sort_func;
191 iface->set_default_sort_func =
192 packet_list_sortable_set_default_sort_func;
193 iface->has_default_sort_func =
194 packet_list_sortable_has_default_sort_func;
198 packet_list_class_init(PacketListClass *klass)
200 GObjectClass *object_class;
202 parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
203 object_class = (GObjectClass*) klass;
205 object_class->finalize = packet_list_finalize;
207 #if !GTK_CHECK_VERSION(3,0,0)
208 /* XXX this seems to affect TreeView Application wide
209 * Move to main.c ??? as it's not a bad thing(tm)
211 gtk_rc_parse_string (
212 "style \"PacketList-style\"\n"
214 " GtkTreeView::horizontal-separator = 0\n"
215 " GtkTreeView::vertical-separator = 1\n"
216 "} widget_class \"*TreeView*\""
217 " style \"PacketList-style\"");
223 packet_list_tree_model_init(GtkTreeModelIface *iface)
225 iface->get_flags = packet_list_get_flags;
226 iface->get_n_columns = packet_list_get_n_columns;
227 iface->get_column_type = packet_list_get_column_type;
228 iface->get_iter = packet_list_get_iter;
229 iface->get_path = packet_list_get_path;
230 iface->get_value = packet_list_get_value;
231 iface->iter_next = packet_list_iter_next;
232 iface->iter_children = packet_list_iter_children;
233 iface->iter_has_child = packet_list_iter_has_child;
234 iface->iter_n_children = packet_list_iter_n_children;
235 iface->iter_nth_child = packet_list_iter_nth_child;
236 iface->iter_parent = packet_list_iter_parent;
239 /* This is called every time a new packet list object instance is created in
240 * packet_list_new. Initialize the list structure's fields here. */
242 packet_list_init(PacketList *packet_list)
246 /* To check whether an iter belongs to our model. */
247 packet_list->stamp = g_random_int();
249 packet_list->n_cols = cfile.cinfo.num_cols;
250 packet_list->physical_rows = g_ptr_array_new();
251 packet_list->visible_rows = g_ptr_array_new();
253 packet_list->columnized = FALSE;
254 packet_list->sort_id = 0; /* defaults to first column for now */
255 packet_list->sort_order = GTK_SORT_ASCENDING;
257 packet_list->col_to_text = g_new(int, packet_list->n_cols);
258 for (i = 0, j = 0; i < packet_list->n_cols; i++) {
259 if (!col_based_on_frame_data(&cfile.cinfo, i)) {
260 packet_list->col_to_text[i] = j;
263 packet_list->col_to_text[i] = -1;
265 packet_list->n_text_cols = j;
267 #ifdef PACKET_LIST_STATISTICS
268 packet_list->const_strings = 0;
272 /* This function is called just before a packet list is destroyed. Free
273 * dynamically allocated memory here. */
275 packet_list_finalize(GObject *object)
277 /* PacketList *packet_list = PACKET_LIST(object); */
279 /* XXX - Free all records and free all memory used by the list */
281 /* must chain up - finalize parent */
282 (* parent_class->finalize) (object);
285 static GtkTreeModelFlags
286 packet_list_get_flags(GtkTreeModel *tree_model)
288 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
289 (GtkTreeModelFlags)0);
291 return (GtkTreeModelFlags)(GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
295 packet_list_get_n_columns(GtkTreeModel *tree_model)
297 PacketList *packet_list;
299 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
300 packet_list = (PacketList *) tree_model;
302 /* Note: We need one extra column to store the entire frame_data */
303 return packet_list->n_cols + 1;
307 packet_list_get_column_type(GtkTreeModel *tree_model, gint idx)
309 PacketList *packet_list;
311 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
312 packet_list = (PacketList *) tree_model;
314 /* Note: We use one extra column to store the entire frame_data */
315 g_return_val_if_fail(idx >= 0 && idx < packet_list->n_cols + 1, G_TYPE_INVALID);
317 if (idx >= 0 && idx < packet_list->n_cols)
318 return G_TYPE_STRING;
319 else if (idx == packet_list->n_cols)
320 return G_TYPE_POINTER;
322 return G_TYPE_INVALID;
326 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
329 gint *indices, depth;
331 g_assert(PACKETLIST_IS_LIST(tree_model));
333 g_assert(path != NULL);
335 indices = gtk_tree_path_get_indices(path);
336 depth = gtk_tree_path_get_depth(path);
338 /* we do not allow children since it's just a list */
339 g_assert(depth == 1);
341 return packet_list_iter_nth_child(tree_model, iter, NULL, indices[0]);
345 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
348 PacketListRecord *record;
349 PacketList *packet_list;
351 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
352 packet_list = (PacketList *) tree_model;
354 g_return_val_if_fail(iter != NULL, NULL);
355 g_return_val_if_fail(iter->stamp == packet_list->stamp, NULL);
356 g_return_val_if_fail(iter->user_data != NULL, NULL);
358 record = (PacketListRecord*) iter->user_data;
360 path = gtk_tree_path_new();
361 gtk_tree_path_append_index(path, record->visible_pos);
367 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
370 PacketListRecord *record;
371 PacketList *packet_list;
373 g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
374 packet_list = (PacketList *) tree_model;
376 g_return_if_fail(iter != NULL);
377 g_return_if_fail(iter->stamp == packet_list->stamp);
378 g_return_if_fail(iter->user_data != NULL);
380 /* Note: We use one extra column to store the entire frame_data */
381 g_return_if_fail(column >= 0 && column < packet_list->n_cols + 1);
383 record = (PacketListRecord*) iter->user_data;
385 #ifdef PACKET_PARANOID_CHECKS
386 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->physical_rows, record->physical_pos));
388 g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, record->visible_pos));
390 if (column >= 0 && column < packet_list->n_cols) {
393 g_value_init(value, G_TYPE_STRING);
395 if (record->col_text == NULL || !record->colorized)
396 packet_list_dissect_and_cache_record(packet_list, record, !record->colorized);
398 text_column = packet_list->col_to_text[column];
399 if (text_column == -1) { /* column based on frame_data */
400 col_fill_in_frame_data(record->fdata, &cfile.cinfo, column, FALSE);
401 g_value_set_string(value, cfile.cinfo.col_data[column]);
403 g_return_if_fail(record->col_text);
404 g_value_set_string(value, record->col_text[text_column]);
407 } else if (column == packet_list->n_cols) {
408 g_value_init(value, G_TYPE_POINTER);
409 g_value_set_pointer(value, record->fdata);
413 static PacketListRecord *
414 packet_list_iter_next_visible(PacketList *packet_list, PacketListRecord *record)
416 PacketListRecord *nextrecord;
417 gint next_visible_pos;
419 g_assert(record->visible_pos >= 0);
420 next_visible_pos = record->visible_pos + 1;
422 /* Is this the last record in the list? */
423 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, next_visible_pos))
426 nextrecord = PACKET_LIST_RECORD_GET(packet_list->visible_rows, next_visible_pos);
428 g_assert(nextrecord->visible_pos == (record->visible_pos + 1));
429 #ifdef PACKET_PARANOID_CHECKS
430 g_assert(nextrecord->physical_pos >= (record->physical_pos + 1));
436 /* Takes an iter structure and sets it to point to the next row. */
438 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
440 PacketListRecord *record, *nextrecord;
441 PacketList *packet_list;
443 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
444 packet_list = (PacketList *) tree_model;
449 g_return_val_if_fail(iter->stamp == packet_list->stamp, FALSE);
450 g_return_val_if_fail(iter->user_data, FALSE);
452 record = (PacketListRecord*) iter->user_data;
453 nextrecord = packet_list_iter_next_visible(packet_list, record);
458 /* iter->stamp = packet_list->stamp; */
459 iter->user_data = nextrecord;
465 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
468 return packet_list_iter_nth_child(tree_model, iter, parent, 0);
472 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
474 return FALSE; /* Lists have no children */
478 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
480 PacketList *packet_list;
482 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
483 packet_list = (PacketList *) tree_model;
486 /* special case: if iter == NULL, return number of top-level rows */
487 return PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
490 g_return_val_if_fail(iter->stamp == packet_list->stamp, 0);
491 g_return_val_if_fail(iter->user_data, 0);
492 /* Lists have zero children */
498 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
499 GtkTreeIter *parent, gint n)
501 PacketListRecord *record;
502 PacketList *packet_list;
504 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
505 packet_list = (PacketList *) tree_model;
507 /* A list only has top-level rows */
509 g_return_val_if_fail(parent->stamp == packet_list->stamp, FALSE);
510 g_return_val_if_fail(parent->user_data, FALSE);
514 /* Special case: if parent == NULL, set iter to n-th top-level row. */
515 if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
518 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
520 g_assert(record->visible_pos == n);
522 iter->stamp = packet_list->stamp;
523 iter->user_data = record;
529 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
530 GtkTreeIter *child _U_)
532 return FALSE; /* No parents since no children in a list */
536 packet_list_new(void)
538 PacketList *newpacketlist;
540 newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
542 g_assert(newpacketlist != NULL);
544 return newpacketlist;
549 packet_list_row_deleted(PacketList *packet_list, guint pos)
553 /* Inform the tree view and other interested objects (such as tree row
554 * references) that we have deleted a row */
555 path = gtk_tree_path_new();
556 gtk_tree_path_append_index(path, pos);
558 gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
560 gtk_tree_path_free(path);
565 packet_list_store_clear(PacketList *packet_list)
567 g_return_if_fail(packet_list != NULL);
568 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
570 /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
571 * the model from the view.
572 for( ; packet_list->num_rows > 0; --packet_list->num_rows)
573 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
576 /* XXX - hold on to these rows and reuse them instead */
577 if(packet_list->physical_rows)
578 g_ptr_array_free(packet_list->physical_rows, TRUE);
579 if(packet_list->visible_rows)
580 g_ptr_array_free(packet_list->visible_rows, TRUE);
581 packet_list->physical_rows = g_ptr_array_new();
582 packet_list->visible_rows = g_ptr_array_new();
584 packet_list->columnized = FALSE;
586 /* Generate new number */
587 packet_list->stamp = g_random_int();
589 #ifdef PACKET_LIST_STATISTICS
590 g_warning("Const strings: %u", packet_list->const_strings);
591 packet_list->const_strings = 0;
596 packet_list_append_record(PacketList *packet_list, frame_data *fdata)
598 PacketListRecord *newrecord;
600 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), -1);
602 newrecord = wmem_new(wmem_file_scope(), PacketListRecord);
603 newrecord->colorized = FALSE;
604 newrecord->col_text_len = NULL;
605 newrecord->col_text = NULL;
606 newrecord->fdata = fdata;
607 #ifdef PACKET_PARANOID_CHECKS
608 newrecord->physical_pos = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
611 if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
612 newrecord->visible_pos = PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
613 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, newrecord);
616 newrecord->visible_pos = -1;
618 PACKET_LIST_RECORD_APPEND(packet_list->physical_rows, newrecord);
620 packet_list->columnized = FALSE; /* XXX, dissect? */
623 * Issue a row_inserted signal if the model is connected
624 * and the row is visible.
626 if (gtk_tree_view_get_model(GTK_TREE_VIEW(packet_list->view)) && newrecord->visible_pos != -1) {
630 path = gtk_tree_path_new();
631 gtk_tree_path_append_index(path, newrecord->visible_pos);
633 iter.stamp = packet_list->stamp;
634 iter.user_data = newrecord;
636 gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
637 gtk_tree_path_free(path);
640 /* XXXX If the model is connected and sort column != frame_num we should
642 * Don't resort the list for every row, the list will be in packet order any way.
643 * packet_list_resort(packet_list);
646 return newrecord->visible_pos;
650 packet_list_change_record(PacketList *packet_list, PacketListRecord *record, gint col, column_info *cinfo)
656 text_col = packet_list->col_to_text[col];
658 /* Column based on frame_data or it already contains a value */
659 if (text_col == -1 || record->col_text[text_col] != NULL)
662 switch (cfile.cinfo.col_fmt[col]) {
664 case COL_RES_SRC: /* COL_DEF_SRC is currently just like COL_RES_SRC */
668 case COL_UNRES_DL_SRC:
669 case COL_DEF_NET_SRC:
670 case COL_RES_NET_SRC:
671 case COL_UNRES_NET_SRC:
673 case COL_RES_DST: /* COL_DEF_DST is currently just like COL_RES_DST */
677 case COL_UNRES_DL_DST:
678 case COL_DEF_NET_DST:
679 case COL_RES_NET_DST:
680 case COL_UNRES_NET_DST:
685 case COL_8021Q_VLAN_ID:
688 if (cinfo->col_data[col] && cinfo->col_data[col] != cinfo->col_buf[col]) {
689 col_text_len = strlen(cinfo->col_data[col]);
690 if (col_text_len > G_MAXUSHORT)
691 col_text_len = G_MAXUSHORT;
693 /* This is a constant string, so we don't have to copy it */
694 record->col_text[text_col] = (gchar *) cinfo->col_data[col];
695 record->col_text_len[text_col] = (gushort) col_text_len;
696 #ifdef PACKET_LIST_STATISTICS
697 ++packet_list->const_strings;
701 /* !! FALL-THROUGH!! */
704 if(cinfo->col_data[col]){
705 col_text_len = strlen(cinfo->col_data[col]);
706 if (col_text_len > G_MAXUSHORT)
707 col_text_len = G_MAXUSHORT;
709 record->col_text_len[text_col] = (gushort) col_text_len;
711 if (!record->col_text_len[text_col]) {
712 record->col_text[text_col] = "";
713 #ifdef PACKET_LIST_STATISTICS
714 ++packet_list->const_strings;
719 if(!packet_list->string_pool)
720 packet_list->string_pool = g_string_chunk_new(32);
721 if (!get_column_resolved (col) && cinfo->col_expr.col_expr_val[col]) {
722 /* Use the unresolved value in col_expr_val */
723 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_expr.col_expr_val[col]);
725 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_data[col]);
727 record->col_text[text_col] = str;
733 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
737 PacketList *packet_list;
739 g_return_val_if_fail(sortable != NULL, FALSE);
740 g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
742 packet_list = (PacketList *) sortable;
745 *sort_col_id = packet_list->sort_id;
748 *order = packet_list->sort_order;
754 packet_list_column_contains_values(PacketList *packet_list, gint sort_col_id)
756 if (packet_list->columnized || col_based_on_frame_data(&cfile.cinfo, sort_col_id))
762 /* packet_list_dissect_and_cache_all()
764 * TRUE if columnization completed;
765 * packet_list->columnized set to TRUE;
766 * FALSE: columnization did not complete (i.e., was stopped by the user);
767 * packet_list->columnized unchanged (i.e., FALSE).
771 packet_list_dissect_and_cache_all(PacketList *packet_list)
773 PacketListRecord *record;
775 int progbar_nextstep;
777 gboolean progbar_stop_flag;
778 GTimeVal progbar_start_time;
780 progdlg_t *progbar = NULL;
781 gchar progbar_status_str[100];
782 gint progbar_loop_max;
783 gint progbar_loop_var;
784 gint progbar_updates = 100 /* 100% */;
786 g_assert(packet_list->columnized == FALSE);
788 progbar_loop_max = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
789 /* Update the progress bar when it gets to this value. */
790 progbar_nextstep = 0;
791 /* When we reach the value that triggers a progress bar update,
792 bump that value by this amount. */
793 progbar_quantum = progbar_loop_max/progbar_updates;
794 /* Progress so far. */
797 progbar_stop_flag = FALSE;
798 g_get_current_time(&progbar_start_time);
800 main_window_update();
802 for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var) {
803 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, progbar_loop_var);
804 packet_list_dissect_and_cache_record(packet_list, record, FALSE);
806 /* Create the progress bar if necessary.
807 We check on every iteration of the loop, so that it takes no
808 longer than the standard time to create it (otherwise, for a
809 large file, we might take considerably longer than that standard
810 time in order to get to the next progress bar step). */
812 /* Note: The following may call gtk_main_iteration() which will */
813 /* allow certain "interupts" to happen during this code. */
814 /* (Note that the progress_dlg window is set to "modal" */
815 /* so that clicking on other windows is disabled). */
816 progbar = delayed_create_progress_dlg(gtk_widget_get_window(packet_list->view),
817 "Construct", "Columns",
818 TRUE, &progbar_stop_flag,
819 &progbar_start_time, progbar_val);
821 if (progbar_loop_var >= progbar_nextstep) {
822 /* let's not divide by zero. We should never be started
823 * with count == 0, so let's assert that */
824 g_assert(progbar_loop_max > 0);
826 progbar_val = (gfloat) progbar_loop_var / progbar_loop_max;
828 if (progbar != NULL) {
829 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
830 "%u of %u frames", progbar_loop_var+1, progbar_loop_max);
831 /* Note: See comment above re use of gtk_main_iteration() */
832 update_progress_dlg(progbar, progbar_val, progbar_status_str);
835 progbar_nextstep += progbar_quantum;
838 if (progbar_stop_flag) {
839 /* Well, the user decided to abort ... */
844 /* We're done; destroy the progress bar if it was created. */
846 destroy_progress_dlg(progbar);
848 if (progbar_stop_flag) {
849 return FALSE; /* user aborted before columnization completed */
852 packet_list->columnized = TRUE;
856 /* packet_list_do_packet_list_dissect_and_cache_all()
858 * TRUE: if columnization not needed or columnization completed;
859 * FALSE: columnization did not complete (i.e., stopped by the user)
862 packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id)
864 if (!packet_list_column_contains_values(packet_list, sort_col_id)) {
865 return packet_list_dissect_and_cache_all(packet_list);
871 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
875 PacketList *packet_list;
877 g_return_if_fail(sortable != NULL);
878 g_return_if_fail(PACKETLIST_IS_LIST(sortable));
880 packet_list = (PacketList *) sortable;
882 if(packet_list->sort_id == sort_col_id &&
883 packet_list->sort_order == order)
886 packet_list->sort_id = sort_col_id;
887 packet_list->sort_order = order;
889 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
892 packet_list_resort(packet_list);
894 /* emit "sort-column-changed" signal to tell any tree views
895 * that the sort column has changed (so the little arrow
896 * in the column header of the sort column is drawn
897 * in the right column) */
899 gtk_tree_sortable_sort_column_changed(sortable);
903 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
904 gint sort_col_id _U_,
905 GtkTreeIterCompareFunc sort_func _U_,
906 gpointer user_data _U_,
907 GDestroyNotify destroy_func _U_)
909 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
913 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
914 GtkTreeIterCompareFunc sort_func _U_,
915 gpointer user_data _U_,
916 GDestroyNotify destroy_func _U_)
918 g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
922 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
924 return FALSE; /* Since packet_list_sortable_set_sort_func and
925 set_default_sort_func are not implemented. */
929 packet_list_compare_custom(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
931 header_field_info *hfi;
933 hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[sort_id]);
936 return frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
937 } else if ((hfi->strings == NULL) &&
938 (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
939 ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
940 (hfi->display == BASE_OCT))) ||
941 (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
942 (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
943 (hfi->type == FT_RELATIVE_TIME)))
945 /* Attempt to convert to numbers */
946 double num_a = g_ascii_strtod(a->col_text[text_sort_id], NULL);
947 double num_b = g_ascii_strtod(b->col_text[text_sort_id], NULL);
951 else if (num_a > num_b)
957 return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
961 _packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
963 g_assert(a->col_text);
964 g_assert(b->col_text);
965 g_assert(a->col_text[text_sort_id]);
966 g_assert(b->col_text[text_sort_id]);
968 if(a->col_text[text_sort_id] == b->col_text[text_sort_id])
969 return 0; /* no need to call strcmp() */
971 if (cfile.cinfo.col_fmt[sort_id] == COL_CUSTOM)
972 return packet_list_compare_custom(sort_id, text_sort_id, a, b);
974 return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
978 packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
982 if (text_sort_id == -1) /* based on frame_data ? */
983 return frame_data_compare(cfile.epan, a->fdata, b->fdata, cfile.cinfo.col_fmt[sort_id]);
985 ret = _packet_list_compare_records(sort_id, text_sort_id, a, b);
987 ret = frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
992 packet_list_qsort_physical_compare_func(PacketListRecord **a, PacketListRecord **b,
993 PacketList *packet_list)
996 gint sort_id = packet_list->sort_id;
998 g_assert((a) && (b) && (packet_list));
1000 ret = packet_list_compare_records(sort_id, packet_list->col_to_text[sort_id], *a, *b);
1002 /* Swap -1 and 1 if sort order is reverse */
1003 if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
1004 ret = (ret < 0) ? 1 : -1;
1010 packet_list_resort(PacketList *packet_list)
1012 PacketListRecord *record;
1018 g_return_if_fail(packet_list != NULL);
1019 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1021 if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1024 /* resort physical rows according to sorting column */
1025 g_ptr_array_sort_with_data(packet_list->physical_rows,
1026 (GCompareDataFunc) packet_list_qsort_physical_compare_func,
1029 /* let other objects know about the new order */
1030 neworder = g_new0(gint, PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1032 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1033 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1034 #ifdef PACKET_PARANOID_CHECKS
1035 record->physical_pos = phy_idx;
1037 g_assert(record->visible_pos >= -1);
1038 if (record->visible_pos >= 0) {
1039 g_assert(record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
1040 neworder[vis_idx] = record->visible_pos;
1041 PACKET_LIST_RECORD_SET(packet_list->visible_rows, vis_idx, record);
1042 record->visible_pos = vis_idx;
1047 g_assert(vis_idx == PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1049 path = gtk_tree_path_new();
1051 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
1054 gtk_tree_path_free(path);
1059 packet_list_recreate_visible_rows_list(PacketList *packet_list)
1063 PacketListRecord *record;
1065 g_return_val_if_fail(packet_list != NULL, 0);
1066 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), 0);
1068 if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
1071 if(packet_list->visible_rows)
1072 g_ptr_array_free(packet_list->visible_rows, TRUE);
1074 packet_list->visible_rows = g_ptr_array_new();
1076 for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1077 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1078 if (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time) {
1079 record->visible_pos = vis_idx++;
1080 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, record);
1083 record->visible_pos = -1;
1090 packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color)
1096 gboolean create_proto_tree;
1097 struct wtap_pkthdr phdr; /* Packet header */
1098 Buffer buf; /* Packet data */
1099 gboolean dissect_columns = (record->col_text == NULL);
1101 g_return_if_fail(packet_list);
1102 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1104 wtap_phdr_init(&phdr);
1106 fdata = record->fdata;
1108 if (dissect_columns) {
1109 cinfo = &cfile.cinfo;
1111 record->col_text = (const gchar **)wmem_alloc0(wmem_file_scope(), sizeof(*record->col_text) * packet_list->n_text_cols);
1112 record->col_text_len = (gushort *)wmem_alloc0(wmem_file_scope(), sizeof(*record->col_text_len) * packet_list->n_text_cols);
1116 ws_buffer_init(&buf, 1500);
1117 if (!cf_read_record_r(&cfile, fdata, &phdr, &buf)) {
1119 * Error reading the record.
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;
1138 ws_buffer_free(&buf);
1139 return; /* error reading the record */
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, cfile.cd_t, &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);
1175 wtap_phdr_cleanup(&phdr);
1176 ws_buffer_free(&buf);
1180 packet_list_reset_colorized(PacketList *packet_list)
1182 PacketListRecord *record;
1185 for(i = 0; i < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++i) {
1186 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, i);
1187 record->colorized = FALSE;
1192 packet_list_get_widest_column_string(PacketList *packet_list, gint col)
1196 g_return_val_if_fail(packet_list != NULL, NULL);
1197 g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), NULL);
1198 /* We need real column here, so not packet_list->n_cols+1 */
1199 g_return_val_if_fail(col >= 0 && col < packet_list->n_cols, NULL);
1201 if (PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1204 text_col = packet_list->col_to_text[col];
1206 if (text_col == -1) { /* column based on frame data */
1207 PacketListRecord *record;
1210 guint widest_packet = 0;
1211 gint widest_column_len = -1;
1213 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1216 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1218 col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1219 column_len = (gint) strlen(cfile.cinfo.col_buf[col]);
1220 if (column_len > widest_column_len) {
1221 widest_column_len = column_len;
1222 widest_packet = vis_idx;
1226 if (widest_column_len != -1) {
1227 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, widest_packet);
1228 col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1230 return cfile.cinfo.col_buf[col];
1235 PacketListRecord *record;
1238 const gchar *widest_column_str = NULL;
1239 guint widest_column_len = 0;
1241 if (!packet_list->columnized)
1242 packet_list_dissect_and_cache_all(packet_list); /* XXX: need to handle case of "incomplete" ? */
1244 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1245 record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1246 if (record->col_text_len[text_col] > widest_column_len) {
1247 widest_column_str = record->col_text[text_col];
1248 widest_column_len = record->col_text_len[text_col];
1252 return widest_column_str;
1257 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1262 * indent-tabs-mode: t
1265 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1266 * :indentSize=8:tabSize=8:noTabs=false: