2 * Routines to implement a custom GTK+ list model for Wireshark's packet list
3 * Copyright 2008-2009, Stephen Fisher <stephentfisher@yahoo.com>
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;
185 packet_list_tree_model_init(GtkTreeModelIface *iface)
187 iface->get_flags = packet_list_get_flags;
188 iface->get_n_columns = packet_list_get_n_columns;
189 iface->get_column_type = packet_list_get_column_type;
190 iface->get_iter = packet_list_get_iter;
191 iface->get_path = packet_list_get_path;
192 iface->get_value = packet_list_get_value;
193 iface->iter_next = packet_list_iter_next;
194 iface->iter_children = packet_list_iter_children;
195 iface->iter_has_child = packet_list_iter_has_child;
196 iface->iter_n_children = packet_list_iter_n_children;
197 iface->iter_nth_child = packet_list_iter_nth_child;
198 iface->iter_parent = packet_list_iter_parent;
201 /* This is called every time a new packet list object instance is created in
202 * packet_list_new. Initialize the list structure's fields here. */
204 packet_list_init(PacketList *packet_list)
209 for(i = 0; i < (guint)cfile.cinfo.num_cols; i++) {
210 /* Get the format of the column, see column_info.h */
211 fmt = get_column_format(i);
213 /* if we wish to store data rater than strings for some
214 * colum types add case statements to the switch.
218 packet_list->column_types[i] = G_TYPE_STRING;
223 packet_list->n_columns = (guint)cfile.cinfo.num_cols;
224 packet_list->num_rows = 0;
225 packet_list->rows = NULL;
227 packet_list->sort_id = 0; /* defaults to first column for now */
228 packet_list->sort_order = GTK_SORT_ASCENDING;
230 packet_list->stamp = g_random_int(); /* To check whether an iter belongs
234 /* This function is called just before a packet list is destroyed. Free
235 * dynamically allocated memory here. */
237 packet_list_finalize(GObject *object)
239 /* PacketList *packet_list = PACKET_LIST(object); */
241 /* XXX - Free all records and free all memory used by the list */
243 /* must chain up - finalize parent */
244 (* parent_class->finalize) (object);
247 static GtkTreeModelFlags
248 packet_list_get_flags(GtkTreeModel *tree_model)
250 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
251 (GtkTreeModelFlags)0);
253 return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
257 packet_list_get_n_columns(GtkTreeModel *tree_model)
259 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
261 return PACKET_LIST(tree_model)->n_columns;
265 packet_list_get_column_type(GtkTreeModel *tree_model, gint index)
267 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
268 g_return_val_if_fail(index < PACKET_LIST(tree_model)->n_columns &&
269 index >= 0, G_TYPE_INVALID);
271 return PACKET_LIST(tree_model)->column_types[index];
275 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
278 PacketList *packet_list;
279 PacketListRecord *record;
280 gint *indices, depth;
283 g_assert(PACKETLIST_IS_LIST(tree_model));
284 g_assert(path != NULL);
286 packet_list = PACKET_LIST(tree_model);
288 indices = gtk_tree_path_get_indices(path);
289 depth = gtk_tree_path_get_depth(path);
291 /* we do not allow children since it's just a list */
292 g_assert(depth == 1);
294 n = indices[0]; /* the n-th top level row */
296 if(n >= packet_list->num_rows)
299 record = packet_list->rows[n];
301 g_assert(record != NULL);
302 g_assert(record->pos == n);
304 /* We simply store a pointer to our custom record in the iter */
305 iter->stamp = packet_list->stamp;
306 iter->user_data = record;
307 iter->user_data2 = NULL;
308 iter->user_data3 = NULL;
314 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
317 PacketListRecord *record;
318 PacketList *packet_list;
320 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
321 g_return_val_if_fail(iter != NULL, NULL);
322 g_return_val_if_fail(iter->user_data != NULL, NULL);
324 packet_list = PACKET_LIST(tree_model);
326 record = (PacketListRecord*) iter->user_data;
328 path = gtk_tree_path_new();
329 gtk_tree_path_append_index(path, record->pos);
335 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
338 PacketListRecord *record;
339 PacketList *packet_list;
341 g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
342 g_return_if_fail(iter != NULL);
343 g_return_if_fail(column < PACKET_LIST(tree_model)->n_columns);
345 g_value_init(value, PACKET_LIST(tree_model)->column_types[column]);
347 packet_list = PACKET_LIST(tree_model);
349 record = (PacketListRecord*) iter->user_data;
351 if(record->pos >= packet_list->num_rows)
352 g_return_if_reached();
354 g_value_set_string(value, record->col_text[column]);
357 /* Takes an iter structure and sets it to point to the next row. */
359 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
361 PacketListRecord *record, *nextrecord;
362 PacketList *packet_list;
364 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
366 if(iter == NULL || iter->user_data == NULL)
369 packet_list = PACKET_LIST(tree_model);
371 record = (PacketListRecord*) iter->user_data;
373 /* Is this the last record in the list? */
374 if((record->pos + 1) >= packet_list->num_rows)
377 nextrecord = packet_list->rows[(record->pos + 1)];
379 g_assert(nextrecord != NULL);
380 g_assert(nextrecord->pos == (record->pos + 1));
382 iter->stamp = packet_list->stamp;
383 iter->user_data = nextrecord;
389 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
392 PacketList *packet_list;
394 g_return_val_if_fail(parent == NULL || parent->user_data != NULL,
397 /* This is a list, nodes have no children. */
401 /* parent == NULL is a special case; we need to return the first top-
404 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
406 packet_list = PACKET_LIST(tree_model);
408 /* No rows => no first row */
409 if(packet_list->num_rows == 0)
412 /* Set iter to first item in list */
413 iter->stamp = packet_list->stamp;
414 iter->user_data = packet_list->rows[0];
420 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
422 return FALSE; /* Lists have no children */
426 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
428 PacketList *packet_list;
430 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), -1);
431 g_return_val_if_fail(iter == NULL || iter->user_data != NULL, FALSE);
433 packet_list = PACKET_LIST(tree_model);
435 /* special case: if iter == NULL, return number of top-level rows */
437 return packet_list->num_rows;
439 return 0; /* Lists have zero children */
443 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
444 GtkTreeIter *parent, gint n)
446 PacketListRecord *record;
447 PacketList *packet_list;
449 g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
451 packet_list = PACKET_LIST(tree_model);
453 /* A list only has top-level rows */
457 /* Special case: if parent == NULL, set iter to n-th
459 if((guint)n >= packet_list->num_rows)
462 record = packet_list->rows[n];
464 g_assert(record != NULL);
465 g_assert(record->pos == (guint)n);
467 iter->stamp = packet_list->stamp;
468 iter->user_data = record;
474 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
475 GtkTreeIter *child _U_)
477 return FALSE; /* No parents since no children in a list */
481 new_packet_list_new(void)
483 PacketList *newpacketlist;
485 newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
487 g_assert(newpacketlist != NULL);
489 return newpacketlist;
493 packet_list_append_record(PacketList *packet_list, row_data_t *row_data)
497 PacketListRecord *newrecord;
501 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
503 pos = packet_list->num_rows;
505 packet_list->num_rows++;
507 packet_list->rows = g_renew(PacketListRecord*, packet_list->rows,
508 packet_list->num_rows);
510 newrecord = se_alloc0(sizeof(PacketListRecord));
511 newrecord->col_text = se_alloc0(sizeof(row_data->col_text)* cfile.cinfo.num_cols);
514 /* XXX newrecord->col_text still uses the fmt index */
515 for(i = 0; i < cfile.cinfo.num_cols; i++)
516 newrecord->col_text[i] = row_data->col_text[i];
518 newrecord->fdata = row_data->fdata;
520 packet_list->rows[pos] = newrecord;
521 newrecord->pos = pos;
523 /* Inform the tree view and other interested objects (such as tree row
524 * references) that we have inserted a new row and where it was
526 path = gtk_tree_path_new();
527 gtk_tree_path_append_index(path, newrecord->pos);
529 packet_list_get_iter(GTK_TREE_MODEL(packet_list), &iter, path);
531 gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
533 gtk_tree_path_free(path);
535 /* Don't resort the list for every row, the list will be in packet order any way.
536 * packet_list_resort(packet_list);
541 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
545 PacketList *packet_list;
547 g_return_val_if_fail(sortable != NULL, FALSE);
548 g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
550 packet_list = PACKET_LIST(sortable);
553 *sort_col_id = packet_list->sort_id;
556 *order = packet_list->sort_order;
562 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
566 PacketList *packet_list;
568 g_return_if_fail(sortable != NULL);
569 g_return_if_fail(PACKETLIST_IS_LIST(sortable));
571 packet_list = PACKET_LIST(sortable);
573 if(packet_list->sort_id == sort_col_id &&
574 packet_list->sort_order == order)
577 packet_list->sort_id = sort_col_id;
578 packet_list->sort_order = order;
580 packet_list_resort(packet_list);
582 /* emit "sort-column-changed" signal to tell any tree views
583 * that the sort column has changed (so the little arrow
584 * in the column header of the sort column is drawn
585 * in the right column) */
587 gtk_tree_sortable_sort_column_changed(sortable);
591 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
592 gint sort_col_id _U_,
593 GtkTreeIterCompareFunc sort_func _U_,
594 gpointer user_data _U_,
595 GtkDestroyNotify destroy_func _U_)
597 g_warning("%s is not supported by the PacketList model.\n",
602 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
603 GtkTreeIterCompareFunc sort_func _U_,
604 gpointer user_data _U_,
605 GtkDestroyNotify destroy_func _U_)
607 g_warning("%s is not supported by the PacketList model.\n",
612 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
614 return FALSE; /* Since packet_list_sortable_set_sort_func and
615 set_default_sort_func are not implemented. */
619 packet_list_compare_records(gint sort_id, PacketListRecord *a,
622 /* The following is necessary because sort_id is a column number as
623 * seen by the user, whereas the col_text array from a and b is a
624 * column format number. This matches them to each other to get
625 * the right column text. */
626 gchar *a_col_text = a->col_text[cfile.cinfo.col_fmt[sort_id]];
627 gchar *b_col_text = b->col_text[cfile.cinfo.col_fmt[sort_id]];
629 if((a_col_text) && (b_col_text))
630 return strcmp(a_col_text, b_col_text);
632 if(a_col_text == b_col_text)
633 return 0; /* both are NULL */
635 return (a_col_text == NULL) ? -1 : 1;
637 g_return_val_if_reached(0);
641 packet_list_qsort_compare_func(PacketListRecord **a, PacketListRecord **b,
642 PacketList *packet_list)
646 g_assert((a) && (b) && (packet_list));
648 ret = packet_list_compare_records(packet_list->sort_id, *a, *b);
650 /* Swap -1 and 1 if sort order is reverse */
651 if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
652 ret = (ret < 0) ? 1 : -1;
658 packet_list_resort(PacketList *packet_list)
664 g_return_if_fail(packet_list != NULL);
665 g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
667 if(packet_list->num_rows == 0)
671 g_qsort_with_data(packet_list->rows, packet_list->num_rows,
672 sizeof(PacketListRecord*),
673 (GCompareDataFunc) packet_list_qsort_compare_func,
676 /* let other objects know about the new order */
677 neworder = g_new0(gint, packet_list->num_rows);
679 for(i = 0; i < packet_list->num_rows; ++i) {
680 neworder[i] = (packet_list->rows[i])->pos;
681 (packet_list->rows[i])->pos = i;
684 path = gtk_tree_path_new();
686 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
689 gtk_tree_path_free(path);
693 #endif /* NEW_PACKET_LIST */