2 * Routines to implement a custom GTK+ list model for Wireshark's packet list
3 * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
27 /* This code is based on the GTK+ Tree View tutorial at http://scentric.net */
34 #ifdef NEW_PACKET_LIST
41 #include "epan/column_info.h"
42 #include "epan/column.h"
44 #include "packet_list_store.h"
47 static void packet_list_init(PacketList *pkg_tree);
48 static void packet_list_class_init(PacketListClass *klass);
49 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
50 static void packet_list_finalize(GObject *object);
51 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
52 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
53 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint index);
54 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
55 GtkTreeIter *iter, GtkTreePath *path);
56 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
58 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
59 gint column, GValue *value);
60 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
62 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
65 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
66 GtkTreeIter *iter _U_);
67 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
69 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
73 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
74 GtkTreeIter *iter _U_,
75 GtkTreeIter *child _U_);
77 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
81 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
84 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
86 GtkTreeIterCompareFunc sort_func,
88 GtkDestroyNotify destroy_func);
89 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
91 GtkTreeIterCompareFunc
96 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
98 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
99 static gint packet_list_compare_records(gint sort_id _U_, PacketListRecord *a,
100 PacketListRecord *b);
101 static gint packet_list_qsort_compare_func(PacketListRecord **a,
102 PacketListRecord **b,
103 PacketList *packet_list);
104 static void packet_list_resort(PacketList *packet_list);
106 static GObjectClass *parent_class = NULL;
110 packet_list_get_type(void)
112 static GType packet_list_type = 0;
114 if(packet_list_type == 0) {
115 static const GTypeInfo packet_list_info = {
116 sizeof(PacketListClass),
117 NULL, /* base_init */
118 NULL, /* base_finalize */
119 (GClassInitFunc) packet_list_class_init,
120 NULL, /* class finalize */
121 NULL, /* class_data */
124 (GInstanceInitFunc) packet_list_init,
125 NULL /* value_table */
128 static const GInterfaceInfo tree_model_info = {
129 (GInterfaceInitFunc) packet_list_tree_model_init,
134 static const GInterfaceInfo tree_sortable_info = {
135 (GInterfaceInitFunc) packet_list_sortable_init,
140 /* Register the new derived type with the GObject type system */
141 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
146 g_type_add_interface_static(packet_list_type,
151 /* Register our GtkTreeModel interface with the type system */
152 g_type_add_interface_static(packet_list_type,
153 GTK_TYPE_TREE_SORTABLE,
154 &tree_sortable_info);
157 return packet_list_type;
161 packet_list_sortable_init(GtkTreeSortableIface *iface)
163 iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
164 iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
165 /* The following three functions are not implemented */
166 iface->set_sort_func = packet_list_sortable_set_sort_func;
167 iface->set_default_sort_func =
168 packet_list_sortable_set_default_sort_func;
169 iface->has_default_sort_func =
170 packet_list_sortable_has_default_sort_func;
174 packet_list_class_init(PacketListClass *klass)
176 GObjectClass *object_class;
178 parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
179 object_class = (GObjectClass*) klass;
181 object_class->finalize = packet_list_finalize;
183 /* XXX this seems to affect TreeView Application wide
184 * Move to main.c ??? as it's not a bad thing(tm)
186 gtk_rc_parse_string (
187 "style \"PacketList-style\"\n"
189 " GtkTreeView::horizontal-separator = 0\n"
190 "} widget_class \"*TreeView*\""
191 " style \"PacketList-style\"");
196 packet_list_tree_model_init(GtkTreeModelIface *iface)
198 iface->get_flags = packet_list_get_flags;
199 iface->get_n_columns = packet_list_get_n_columns;
200 iface->get_column_type = packet_list_get_column_type;
201 iface->get_iter = packet_list_get_iter;
202 iface->get_path = packet_list_get_path;
203 iface->get_value = packet_list_get_value;
204 iface->iter_next = packet_list_iter_next;
205 iface->iter_children = packet_list_iter_children;
206 iface->iter_has_child = packet_list_iter_has_child;
207 iface->iter_n_children = packet_list_iter_n_children;
208 iface->iter_nth_child = packet_list_iter_nth_child;
209 iface->iter_parent = packet_list_iter_parent;
212 /* This is called every time a new packet list object instance is created in
213 * packet_list_new. Initialize the list structure's fields here. */
215 packet_list_init(PacketList *packet_list)
220 for(i = 0; i < (guint)cfile.cinfo.num_cols; i++) {
221 /* Get the format of the column, see column_info.h */
222 fmt = get_column_format(i);
224 /* if we wish to store data rater than strings for some
225 * colum types add case statements to the switch.
229 packet_list->column_types[i] = G_TYPE_STRING;
234 packet_list->n_columns = (guint)cfile.cinfo.num_cols;
235 packet_list->num_rows = 0;
236 packet_list->rows = NULL;
238 packet_list->sort_id = 0; /* defaults to first column for now */
239 packet_list->sort_order = GTK_SORT_ASCENDING;
241 packet_list->stamp = g_random_int(); /* To check whether an iter belongs
245 /* This function is called just before a packet list is destroyed. Free
246 * dynamically allocated memory here. */
248 packet_list_finalize(GObject *object)
250 /* PacketList *packet_list = PACKET_LIST(object); */
252 /* XXX - Free all records and free all memory used by the list */
254 /* must chain up - finalize parent */
255 (* parent_class->finalize) (object);
258 static GtkTreeModelFlags
259 packet_list_get_flags(GtkTreeModel *tree_model)
261 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
262 (GtkTreeModelFlags)0);
264 return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
268 packet_list_get_n_columns(GtkTreeModel *tree_model)
270 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
272 return PACKET_LIST(tree_model)->n_columns;
276 packet_list_get_column_type(GtkTreeModel *tree_model, gint index)
278 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
279 g_return_val_if_fail(index < PACKET_LIST(tree_model)->n_columns &&
280 index >= 0, G_TYPE_INVALID);
282 return PACKET_LIST(tree_model)->column_types[index];
286 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
289 PacketList *packet_list;
290 PacketListRecord *record;
291 gint *indices, depth;
294 g_assert(PACKETLIST_IS_LIST(tree_model));
295 g_assert(path != NULL);
297 packet_list = PACKET_LIST(tree_model);
299 indices = gtk_tree_path_get_indices(path);
300 depth = gtk_tree_path_get_depth(path);
302 /* we do not allow children since it's just a list */
303 g_assert(depth == 1);
305 n = indices[0]; /* the n-th top level row */
307 if(n >= packet_list->num_rows)
310 record = packet_list->rows[n];
312 g_assert(record != NULL);
313 g_assert(record->pos == n);
315 /* We simply store a pointer to our custom record in the iter */
316 iter->stamp = packet_list->stamp;
317 iter->user_data = record;
318 iter->user_data2 = NULL;
319 iter->user_data3 = NULL;
325 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
328 PacketListRecord *record;
329 PacketList *packet_list;
331 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
332 g_return_val_if_fail(iter != NULL, NULL);
333 g_return_val_if_fail(iter->user_data != NULL, NULL);
335 packet_list = PACKET_LIST(tree_model);
337 record = (PacketListRecord*) iter->user_data;
339 path = gtk_tree_path_new();
340 gtk_tree_path_append_index(path, record->pos);
346 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
349 PacketListRecord *record;
350 PacketList *packet_list;
353 g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
354 g_return_if_fail(iter != NULL);
355 g_return_if_fail(column < PACKET_LIST(tree_model)->n_columns);
357 type = PACKET_LIST(tree_model)->column_types[column];
358 g_value_init(value, type);
360 packet_list = PACKET_LIST(tree_model);
362 record = (PacketListRecord*) iter->user_data;
364 if(record->pos >= packet_list->num_rows)
365 g_return_if_reached();
367 /* XXX Probably the switch should be on column or
368 * should we allways return the pointer and read the data as required??
369 * If we use FOREGROUND_COLOR_COL etc we'll need a couple of "internal" columns
373 g_value_set_pointer(value, record);
376 g_value_set_string(value, record->col_text[column]);
379 g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
384 /* Takes an iter structure and sets it to point to the next row. */
386 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
388 PacketListRecord *record, *nextrecord;
389 PacketList *packet_list;
391 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
393 if(iter == NULL || iter->user_data == NULL)
396 packet_list = PACKET_LIST(tree_model);
398 record = (PacketListRecord*) iter->user_data;
400 /* Is this the last record in the list? */
401 if((record->pos + 1) >= packet_list->num_rows)
404 nextrecord = packet_list->rows[(record->pos + 1)];
406 g_assert(nextrecord != NULL);
407 g_assert(nextrecord->pos == (record->pos + 1));
409 iter->stamp = packet_list->stamp;
410 iter->user_data = nextrecord;
416 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
419 PacketList *packet_list;
421 g_return_val_if_fail(parent == NULL || parent->user_data != NULL,
424 /* This is a list, nodes have no children. */
428 /* parent == NULL is a special case; we need to return the first top-
431 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
433 packet_list = PACKET_LIST(tree_model);
435 /* No rows => no first row */
436 if(packet_list->num_rows == 0)
439 /* Set iter to first item in list */
440 iter->stamp = packet_list->stamp;
441 iter->user_data = packet_list->rows[0];
447 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
449 return FALSE; /* Lists have no children */
453 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
455 PacketList *packet_list;
457 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), -1);
458 g_return_val_if_fail(iter == NULL || iter->user_data != NULL, FALSE);
460 packet_list = PACKET_LIST(tree_model);
462 /* special case: if iter == NULL, return number of top-level rows */
464 return packet_list->num_rows;
466 return 0; /* Lists have zero children */
470 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
471 GtkTreeIter *parent, gint n)
473 PacketListRecord *record;
474 PacketList *packet_list;
476 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
478 packet_list = PACKET_LIST(tree_model);
480 /* A list only has top-level rows */
484 /* Special case: if parent == NULL, set iter to n-th
486 if((guint)n >= packet_list->num_rows)
489 record = packet_list->rows[n];
491 g_assert(record != NULL);
492 g_assert(record->pos == (guint)n);
494 iter->stamp = packet_list->stamp;
495 iter->user_data = record;
501 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
502 GtkTreeIter *child _U_)
504 return FALSE; /* No parents since no children in a list */
508 new_packet_list_new(void)
510 PacketList *newpacketlist;
512 newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
514 g_assert(newpacketlist != NULL);
516 return newpacketlist;
521 packet_list_row_deleted(PacketList *packet_list, guint pos)
525 /* Inform the tree view and other interested objects (such as tree row
526 * references) that we have deleted a row */
527 path = gtk_tree_path_new();
528 gtk_tree_path_append_index(path, pos);
530 gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
532 gtk_tree_path_free(path);
537 new_packet_list_store_clear(PacketList *packet_list)
539 g_return_if_fail(packet_list != NULL);
540 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
542 if(packet_list->num_rows == 0)
545 /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
546 * the model from the view.
547 for( ; packet_list->num_rows > 0; --packet_list->num_rows)
548 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
551 /* XXX - hold on to these rows and reuse them instead */
552 g_free(packet_list->rows);
553 packet_list->rows = NULL;
554 packet_list->num_rows = 0;
559 packet_list_row_inserted(PacketList *packet_list, guint pos)
564 /* Inform the tree view and other interested objects (such as tree row
565 * references) that we have inserted a new row and where it was
567 path = gtk_tree_path_new();
568 gtk_tree_path_append_index(path, pos);
570 packet_list_get_iter(GTK_TREE_MODEL(packet_list), &iter, path);
572 gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
574 gtk_tree_path_free(path);
579 packet_list_append_record(PacketList *packet_list, row_data_t *row_data)
581 PacketListRecord *newrecord;
584 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
586 pos = packet_list->num_rows;
588 packet_list->num_rows++;
590 packet_list->rows = g_renew(PacketListRecord*, packet_list->rows,
591 packet_list->num_rows);
593 newrecord = se_alloc(sizeof(PacketListRecord));
594 newrecord->dissected = FALSE;
595 newrecord->col_text = row_data->col_text;
596 newrecord->fdata = row_data->fdata;
597 newrecord->pos = pos;
599 packet_list->rows[pos] = newrecord;
601 /* Don't issue a row_inserted signal. We rely on our caller to have disconnected
602 * the model from the view.
603 * packet_list_row_inserted(packet_list, newrecord->pos);
606 /* Don't resort the list for every row, the list will be in packet order any way.
607 * packet_list_resort(packet_list);
612 packet_list_change_record(PacketList *packet_list, guint row, gint col, column_info *cinfo)
614 PacketListRecord *record;
616 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
618 g_assert(row < packet_list->num_rows);
619 record = packet_list->rows[row];
620 g_assert(record->pos == row);
621 g_assert(!record->col_text || (record->col_text[col] == NULL));
622 if (!record->col_text)
623 record->col_text = se_alloc0(sizeof(record->col_text)*packet_list->n_columns);
625 record->col_text[col] = se_strdup(cinfo->col_data[col]);
629 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
633 PacketList *packet_list;
635 g_return_val_if_fail(sortable != NULL, FALSE);
636 g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
638 packet_list = PACKET_LIST(sortable);
641 *sort_col_id = packet_list->sort_id;
644 *order = packet_list->sort_order;
650 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
654 PacketList *packet_list;
656 g_return_if_fail(sortable != NULL);
657 g_return_if_fail(PACKETLIST_IS_LIST(sortable));
659 packet_list = PACKET_LIST(sortable);
661 if(packet_list->sort_id == sort_col_id &&
662 packet_list->sort_order == order)
665 packet_list->sort_id = sort_col_id;
666 packet_list->sort_order = order;
668 packet_list_resort(packet_list);
670 /* emit "sort-column-changed" signal to tell any tree views
671 * that the sort column has changed (so the little arrow
672 * in the column header of the sort column is drawn
673 * in the right column) */
675 gtk_tree_sortable_sort_column_changed(sortable);
679 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
680 gint sort_col_id _U_,
681 GtkTreeIterCompareFunc sort_func _U_,
682 gpointer user_data _U_,
683 GtkDestroyNotify destroy_func _U_)
685 g_warning("%s is not supported by the PacketList model.\n",
690 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
691 GtkTreeIterCompareFunc sort_func _U_,
692 gpointer user_data _U_,
693 GtkDestroyNotify destroy_func _U_)
695 g_warning("%s is not supported by the PacketList model.\n",
700 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
702 return FALSE; /* Since packet_list_sortable_set_sort_func and
703 set_default_sort_func are not implemented. */
707 packet_list_compare_records(gint sort_id, PacketListRecord *a,
711 /* XXX If we want to store other things than text, we need other sort functions */
713 if (col_based_on_frame_data(&cfile.cinfo, sort_id))
714 return frame_data_compare(a->fdata, b->fdata, cfile.cinfo.col_fmt[sort_id]);
716 if((a->col_text[sort_id]) && (b->col_text[sort_id]))
717 return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
719 if(a->col_text[sort_id] == b->col_text[sort_id])
720 return 0; /* both are NULL */
722 return (a->col_text[sort_id] == NULL) ? -1 : 1;
724 g_return_val_if_reached(0);
727 packet_list_qsort_compare_func(PacketListRecord **a, PacketListRecord **b,
728 PacketList *packet_list)
732 g_assert((a) && (b) && (packet_list));
734 ret = packet_list_compare_records(packet_list->sort_id, *a, *b);
736 /* Swap -1 and 1 if sort order is reverse */
737 if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
738 ret = (ret < 0) ? 1 : -1;
744 packet_list_resort(PacketList *packet_list)
750 g_return_if_fail(packet_list != NULL);
751 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
753 if(packet_list->num_rows == 0)
757 g_qsort_with_data(packet_list->rows, packet_list->num_rows,
758 sizeof(PacketListRecord*),
759 (GCompareDataFunc) packet_list_qsort_compare_func,
762 /* let other objects know about the new order */
763 neworder = g_new0(gint, packet_list->num_rows);
765 for(i = 0; i < packet_list->num_rows; ++i) {
766 neworder[i] = (packet_list->rows[i])->pos;
767 (packet_list->rows[i])->pos = i;
770 path = gtk_tree_path_new();
772 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
775 gtk_tree_path_free(path);
779 #endif /* NEW_PACKET_LIST */