712 lines
19 KiB
C
712 lines
19 KiB
C
/* packet_list_store.c
|
|
* Routines to implement a custom GTK+ list model for Wireshark's packet list
|
|
* Copyright 2008-2009, Stephen Fisher <stephentfisher@yahoo.com>
|
|
*
|
|
* $Id$
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
* USA.
|
|
*/
|
|
|
|
/* This code is based on the GTK+ Tree View tutorial at http://scentric.net */
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef NEW_PACKET_LIST
|
|
|
|
#include <string.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
|
|
#include "epan/column_info.h"
|
|
#include "epan/column.h"
|
|
|
|
#include "packet_list_store.h"
|
|
#include "globals.h"
|
|
|
|
static void packet_list_init(PacketList *pkg_tree);
|
|
static void packet_list_class_init(PacketListClass *klass);
|
|
static void packet_list_tree_model_init(GtkTreeModelIface *iface);
|
|
static void packet_list_finalize(GObject *object);
|
|
static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
|
|
static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
|
|
static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint index);
|
|
static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter, GtkTreePath *path);
|
|
static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
|
|
gint column, GValue *value);
|
|
static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent);
|
|
static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
|
|
GtkTreeIter *iter _U_);
|
|
static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent,
|
|
gint n);
|
|
static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
|
|
GtkTreeIter *iter _U_,
|
|
GtkTreeIter *child _U_);
|
|
|
|
static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
|
|
*sortable,
|
|
gint *sort_col_id,
|
|
GtkSortType *order);
|
|
static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
|
|
gint sort_col_id,
|
|
GtkSortType order);
|
|
static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
|
|
gint sort_col_id,
|
|
GtkTreeIterCompareFunc sort_func,
|
|
gpointer user_data,
|
|
GtkDestroyNotify destroy_func);
|
|
static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
|
|
*sortable,
|
|
GtkTreeIterCompareFunc
|
|
sort_func,
|
|
gpointer user_data,
|
|
GtkDestroyNotify
|
|
destroy_func);
|
|
static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
|
|
*sortable);
|
|
static void packet_list_sortable_init(GtkTreeSortableIface *iface);
|
|
static gint packet_list_compare_records(gint sort_id _U_, PacketListRecord *a,
|
|
PacketListRecord *b);
|
|
static gint packet_list_qsort_compare_func(PacketListRecord **a,
|
|
PacketListRecord **b,
|
|
PacketList *packet_list);
|
|
static void packet_list_resort(PacketList *packet_list);
|
|
|
|
static GObjectClass *parent_class = NULL;
|
|
|
|
|
|
GType
|
|
packet_list_get_type(void)
|
|
{
|
|
static GType packet_list_type = 0;
|
|
|
|
if(packet_list_type == 0) {
|
|
static const GTypeInfo packet_list_info = {
|
|
sizeof(PacketListClass),
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
(GClassInitFunc) packet_list_class_init,
|
|
NULL, /* class finalize */
|
|
NULL, /* class_data */
|
|
sizeof(PacketList),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) packet_list_init,
|
|
NULL /* value_table */
|
|
};
|
|
|
|
static const GInterfaceInfo tree_model_info = {
|
|
(GInterfaceInitFunc) packet_list_tree_model_init,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
static const GInterfaceInfo tree_sortable_info = {
|
|
(GInterfaceInitFunc) packet_list_sortable_init,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
/* Register the new derived type with the GObject type system */
|
|
packet_list_type = g_type_register_static(G_TYPE_OBJECT,
|
|
"PacketList",
|
|
&packet_list_info,
|
|
(GTypeFlags)0);
|
|
|
|
g_type_add_interface_static(packet_list_type,
|
|
GTK_TYPE_TREE_MODEL,
|
|
&tree_model_info);
|
|
|
|
|
|
/* Register our GtkTreeModel interface with the type system */
|
|
g_type_add_interface_static(packet_list_type,
|
|
GTK_TYPE_TREE_SORTABLE,
|
|
&tree_sortable_info);
|
|
}
|
|
|
|
return packet_list_type;
|
|
}
|
|
|
|
static void
|
|
packet_list_sortable_init(GtkTreeSortableIface *iface)
|
|
{
|
|
iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
|
|
iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
|
|
/* The following three functions are not implemented */
|
|
iface->set_sort_func = packet_list_sortable_set_sort_func;
|
|
iface->set_default_sort_func =
|
|
packet_list_sortable_set_default_sort_func;
|
|
iface->has_default_sort_func =
|
|
packet_list_sortable_has_default_sort_func;
|
|
}
|
|
|
|
static void
|
|
packet_list_class_init(PacketListClass *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
|
|
object_class = (GObjectClass*) klass;
|
|
|
|
object_class->finalize = packet_list_finalize;
|
|
}
|
|
|
|
static void
|
|
packet_list_tree_model_init(GtkTreeModelIface *iface)
|
|
{
|
|
iface->get_flags = packet_list_get_flags;
|
|
iface->get_n_columns = packet_list_get_n_columns;
|
|
iface->get_column_type = packet_list_get_column_type;
|
|
iface->get_iter = packet_list_get_iter;
|
|
iface->get_path = packet_list_get_path;
|
|
iface->get_value = packet_list_get_value;
|
|
iface->iter_next = packet_list_iter_next;
|
|
iface->iter_children = packet_list_iter_children;
|
|
iface->iter_has_child = packet_list_iter_has_child;
|
|
iface->iter_n_children = packet_list_iter_n_children;
|
|
iface->iter_nth_child = packet_list_iter_nth_child;
|
|
iface->iter_parent = packet_list_iter_parent;
|
|
}
|
|
|
|
/* This is called every time a new packet list object instance is created in
|
|
* packet_list_new. Initialize the list structure's fields here. */
|
|
static void
|
|
packet_list_init(PacketList *packet_list)
|
|
{
|
|
guint i;
|
|
gint fmt;
|
|
|
|
for(i = 0; i < (guint)cfile.cinfo.num_cols; i++) {
|
|
/* Get the format of the column, see column_info.h */
|
|
fmt = get_column_format(i);
|
|
switch(fmt){
|
|
/* if we wish to store data rater than strings for some
|
|
* colum types add case statements to the switch.
|
|
*/
|
|
case COL_NUMBER:
|
|
default:
|
|
packet_list->column_types[i] = G_TYPE_STRING;
|
|
break;
|
|
}
|
|
}
|
|
|
|
packet_list->n_columns = (guint)cfile.cinfo.num_cols;
|
|
packet_list->num_rows = 0;
|
|
packet_list->rows = NULL;
|
|
|
|
packet_list->sort_id = 0; /* defaults to first column for now */
|
|
packet_list->sort_order = GTK_SORT_ASCENDING;
|
|
|
|
packet_list->stamp = g_random_int(); /* To check whether an iter belongs
|
|
* to our model. */
|
|
}
|
|
|
|
/* This function is called just before a packet list is destroyed. Free
|
|
* dynamically allocated memory here. */
|
|
static void
|
|
packet_list_finalize(GObject *object)
|
|
{
|
|
/* PacketList *packet_list = PACKET_LIST(object); */
|
|
|
|
/* XXX - Free all records and free all memory used by the list */
|
|
|
|
/* must chain up - finalize parent */
|
|
(* parent_class->finalize) (object);
|
|
}
|
|
|
|
static GtkTreeModelFlags
|
|
packet_list_get_flags(GtkTreeModel *tree_model)
|
|
{
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
|
|
(GtkTreeModelFlags)0);
|
|
|
|
return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
|
|
}
|
|
|
|
static gint
|
|
packet_list_get_n_columns(GtkTreeModel *tree_model)
|
|
{
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
|
|
|
|
return PACKET_LIST(tree_model)->n_columns;
|
|
}
|
|
|
|
static GType
|
|
packet_list_get_column_type(GtkTreeModel *tree_model, gint index)
|
|
{
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
|
|
g_return_val_if_fail(index < PACKET_LIST(tree_model)->n_columns &&
|
|
index >= 0, G_TYPE_INVALID);
|
|
|
|
return PACKET_LIST(tree_model)->column_types[index];
|
|
}
|
|
|
|
static gboolean
|
|
packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
|
|
GtkTreePath *path)
|
|
{
|
|
PacketList *packet_list;
|
|
PacketListRecord *record;
|
|
gint *indices, depth;
|
|
guint n;
|
|
|
|
g_assert(PACKETLIST_IS_LIST(tree_model));
|
|
g_assert(path != NULL);
|
|
|
|
packet_list = PACKET_LIST(tree_model);
|
|
|
|
indices = gtk_tree_path_get_indices(path);
|
|
depth = gtk_tree_path_get_depth(path);
|
|
|
|
/* we do not allow children since it's just a list */
|
|
g_assert(depth == 1);
|
|
|
|
n = indices[0]; /* the n-th top level row */
|
|
|
|
if(n >= packet_list->num_rows)
|
|
return FALSE;
|
|
|
|
record = packet_list->rows[n];
|
|
|
|
g_assert(record != NULL);
|
|
g_assert(record->pos == n);
|
|
|
|
/* We simply store a pointer to our custom record in the iter */
|
|
iter->stamp = packet_list->stamp;
|
|
iter->user_data = record;
|
|
iter->user_data2 = NULL;
|
|
iter->user_data3 = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GtkTreePath *
|
|
packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
|
|
{
|
|
GtkTreePath *path;
|
|
PacketListRecord *record;
|
|
PacketList *packet_list;
|
|
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
|
|
g_return_val_if_fail(iter != NULL, NULL);
|
|
g_return_val_if_fail(iter->user_data != NULL, NULL);
|
|
|
|
packet_list = PACKET_LIST(tree_model);
|
|
|
|
record = (PacketListRecord*) iter->user_data;
|
|
|
|
path = gtk_tree_path_new();
|
|
gtk_tree_path_append_index(path, record->pos);
|
|
|
|
return path;
|
|
}
|
|
|
|
static void
|
|
packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
|
|
GValue *value)
|
|
{
|
|
PacketListRecord *record;
|
|
PacketList *packet_list;
|
|
GType type;
|
|
|
|
g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
|
|
g_return_if_fail(iter != NULL);
|
|
g_return_if_fail(column < PACKET_LIST(tree_model)->n_columns);
|
|
|
|
type = PACKET_LIST(tree_model)->column_types[column];
|
|
g_value_init(value, type);
|
|
|
|
packet_list = PACKET_LIST(tree_model);
|
|
|
|
record = (PacketListRecord*) iter->user_data;
|
|
|
|
if(record->pos >= packet_list->num_rows)
|
|
g_return_if_reached();
|
|
|
|
/* XXX Probably the switch should be on column or
|
|
* should we allways return the pointer and read the data as required??
|
|
* If we use FOREGROUND_COLOR_COL etc we'll need a couple of "internal" columns
|
|
*/
|
|
switch(type){
|
|
case G_TYPE_POINTER:
|
|
g_value_set_pointer(value, record);
|
|
break;
|
|
case G_TYPE_STRING:
|
|
g_value_set_string(value, record->col_text[column]);
|
|
break;
|
|
default:
|
|
g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Takes an iter structure and sets it to point to the next row. */
|
|
static gboolean
|
|
packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
|
|
{
|
|
PacketListRecord *record, *nextrecord;
|
|
PacketList *packet_list;
|
|
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
|
|
|
|
if(iter == NULL || iter->user_data == NULL)
|
|
return FALSE;
|
|
|
|
packet_list = PACKET_LIST(tree_model);
|
|
|
|
record = (PacketListRecord*) iter->user_data;
|
|
|
|
/* Is this the last record in the list? */
|
|
if((record->pos + 1) >= packet_list->num_rows)
|
|
return FALSE;
|
|
|
|
nextrecord = packet_list->rows[(record->pos + 1)];
|
|
|
|
g_assert(nextrecord != NULL);
|
|
g_assert(nextrecord->pos == (record->pos + 1));
|
|
|
|
iter->stamp = packet_list->stamp;
|
|
iter->user_data = nextrecord;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
|
|
GtkTreeIter *parent)
|
|
{
|
|
PacketList *packet_list;
|
|
|
|
g_return_val_if_fail(parent == NULL || parent->user_data != NULL,
|
|
FALSE);
|
|
|
|
/* This is a list, nodes have no children. */
|
|
if(parent)
|
|
return FALSE;
|
|
|
|
/* parent == NULL is a special case; we need to return the first top-
|
|
* level row */
|
|
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
|
|
|
|
packet_list = PACKET_LIST(tree_model);
|
|
|
|
/* No rows => no first row */
|
|
if(packet_list->num_rows == 0)
|
|
return FALSE;
|
|
|
|
/* Set iter to first item in list */
|
|
iter->stamp = packet_list->stamp;
|
|
iter->user_data = packet_list->rows[0];
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
|
|
{
|
|
return FALSE; /* Lists have no children */
|
|
}
|
|
|
|
static gint
|
|
packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
|
|
{
|
|
PacketList *packet_list;
|
|
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), -1);
|
|
g_return_val_if_fail(iter == NULL || iter->user_data != NULL, FALSE);
|
|
|
|
packet_list = PACKET_LIST(tree_model);
|
|
|
|
/* special case: if iter == NULL, return number of top-level rows */
|
|
if(!iter)
|
|
return packet_list->num_rows;
|
|
|
|
return 0; /* Lists have zero children */
|
|
}
|
|
|
|
static gboolean
|
|
packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
|
|
GtkTreeIter *parent, gint n)
|
|
{
|
|
PacketListRecord *record;
|
|
PacketList *packet_list;
|
|
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
|
|
|
|
packet_list = PACKET_LIST(tree_model);
|
|
|
|
/* A list only has top-level rows */
|
|
if(parent)
|
|
return FALSE;
|
|
|
|
/* Special case: if parent == NULL, set iter to n-th
|
|
* top-level row. */
|
|
if((guint)n >= packet_list->num_rows)
|
|
return FALSE;
|
|
|
|
record = packet_list->rows[n];
|
|
|
|
g_assert(record != NULL);
|
|
g_assert(record->pos == (guint)n);
|
|
|
|
iter->stamp = packet_list->stamp;
|
|
iter->user_data = record;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
|
|
GtkTreeIter *child _U_)
|
|
{
|
|
return FALSE; /* No parents since no children in a list */
|
|
}
|
|
|
|
PacketList *
|
|
new_packet_list_new(void)
|
|
{
|
|
PacketList *newpacketlist;
|
|
|
|
newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
|
|
|
|
g_assert(newpacketlist != NULL);
|
|
|
|
return newpacketlist;
|
|
}
|
|
|
|
void
|
|
packet_list_append_record(PacketList *packet_list, row_data_t *row_data)
|
|
{
|
|
GtkTreeIter iter;
|
|
GtkTreePath *path;
|
|
PacketListRecord *newrecord;
|
|
guint pos;
|
|
gint i;
|
|
|
|
g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
|
|
|
|
pos = packet_list->num_rows;
|
|
|
|
packet_list->num_rows++;
|
|
|
|
packet_list->rows = g_renew(PacketListRecord*, packet_list->rows,
|
|
packet_list->num_rows);
|
|
|
|
newrecord = se_alloc0(sizeof(PacketListRecord));
|
|
newrecord->col_text = se_alloc0(sizeof(row_data->col_text)* cfile.cinfo.num_cols);
|
|
|
|
|
|
/* XXX newrecord->col_text still uses the fmt index */
|
|
for(i = 0; i < cfile.cinfo.num_cols; i++)
|
|
newrecord->col_text[i] = row_data->col_text[i];
|
|
|
|
newrecord->fdata = row_data->fdata;
|
|
|
|
packet_list->rows[pos] = newrecord;
|
|
newrecord->pos = pos;
|
|
|
|
/* Inform the tree view and other interested objects (such as tree row
|
|
* references) that we have inserted a new row and where it was
|
|
* inserted. */
|
|
path = gtk_tree_path_new();
|
|
gtk_tree_path_append_index(path, newrecord->pos);
|
|
|
|
packet_list_get_iter(GTK_TREE_MODEL(packet_list), &iter, path);
|
|
|
|
gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
|
|
|
|
gtk_tree_path_free(path);
|
|
|
|
/* Don't resort the list for every row, the list will be in packet order any way.
|
|
* packet_list_resort(packet_list);
|
|
*/
|
|
}
|
|
|
|
static gboolean
|
|
packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
|
|
gint *sort_col_id,
|
|
GtkSortType *order)
|
|
{
|
|
PacketList *packet_list;
|
|
|
|
g_return_val_if_fail(sortable != NULL, FALSE);
|
|
g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
|
|
|
|
packet_list = PACKET_LIST(sortable);
|
|
|
|
if(sort_col_id)
|
|
*sort_col_id = packet_list->sort_id;
|
|
|
|
if(order)
|
|
*order = packet_list->sort_order;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
|
|
gint sort_col_id,
|
|
GtkSortType order)
|
|
{
|
|
PacketList *packet_list;
|
|
|
|
g_return_if_fail(sortable != NULL);
|
|
g_return_if_fail(PACKETLIST_IS_LIST(sortable));
|
|
|
|
packet_list = PACKET_LIST(sortable);
|
|
|
|
if(packet_list->sort_id == sort_col_id &&
|
|
packet_list->sort_order == order)
|
|
return;
|
|
|
|
packet_list->sort_id = sort_col_id;
|
|
packet_list->sort_order = order;
|
|
|
|
packet_list_resort(packet_list);
|
|
|
|
/* emit "sort-column-changed" signal to tell any tree views
|
|
* that the sort column has changed (so the little arrow
|
|
* in the column header of the sort column is drawn
|
|
* in the right column) */
|
|
|
|
gtk_tree_sortable_sort_column_changed(sortable);
|
|
}
|
|
|
|
static void
|
|
packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
|
|
gint sort_col_id _U_,
|
|
GtkTreeIterCompareFunc sort_func _U_,
|
|
gpointer user_data _U_,
|
|
GtkDestroyNotify destroy_func _U_)
|
|
{
|
|
g_warning("%s is not supported by the PacketList model.\n",
|
|
__FUNCTION__);
|
|
}
|
|
|
|
static void
|
|
packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
|
|
GtkTreeIterCompareFunc sort_func _U_,
|
|
gpointer user_data _U_,
|
|
GtkDestroyNotify destroy_func _U_)
|
|
{
|
|
g_warning("%s is not supported by the PacketList model.\n",
|
|
__FUNCTION__);
|
|
}
|
|
|
|
static gboolean
|
|
packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
|
|
{
|
|
return FALSE; /* Since packet_list_sortable_set_sort_func and
|
|
set_default_sort_func are not implemented. */
|
|
}
|
|
|
|
static gint
|
|
packet_list_compare_records(gint sort_id, PacketListRecord *a,
|
|
PacketListRecord *b)
|
|
{
|
|
|
|
/* XXX If we want to store other things than text, we need other sort functions */
|
|
|
|
/* Get the frame number from frame data
|
|
* Is this a bit hackish??
|
|
*/
|
|
if( cfile.cinfo.col_fmt[sort_id]==COL_NUMBER){
|
|
return (b->fdata->num - a->fdata->num);
|
|
}
|
|
|
|
if((a->col_text[sort_id]) && (b->col_text[sort_id]))
|
|
return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
|
|
|
|
if(a->col_text[sort_id] == b->col_text[sort_id])
|
|
return 0; /* both are NULL */
|
|
else
|
|
return (a->col_text[sort_id] == NULL) ? -1 : 1;
|
|
|
|
g_return_val_if_reached(0);
|
|
}
|
|
static gint
|
|
packet_list_qsort_compare_func(PacketListRecord **a, PacketListRecord **b,
|
|
PacketList *packet_list)
|
|
{
|
|
gint ret;
|
|
|
|
g_assert((a) && (b) && (packet_list));
|
|
|
|
ret = packet_list_compare_records(packet_list->sort_id, *a, *b);
|
|
|
|
/* Swap -1 and 1 if sort order is reverse */
|
|
if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
|
|
ret = (ret < 0) ? 1 : -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
packet_list_resort(PacketList *packet_list)
|
|
{
|
|
GtkTreePath *path;
|
|
gint *neworder;
|
|
guint i;
|
|
|
|
g_return_if_fail(packet_list != NULL);
|
|
g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
|
|
|
|
if(packet_list->num_rows == 0)
|
|
return;
|
|
|
|
/* resort */
|
|
g_qsort_with_data(packet_list->rows, packet_list->num_rows,
|
|
sizeof(PacketListRecord*),
|
|
(GCompareDataFunc) packet_list_qsort_compare_func,
|
|
packet_list);
|
|
|
|
/* let other objects know about the new order */
|
|
neworder = g_new0(gint, packet_list->num_rows);
|
|
|
|
for(i = 0; i < packet_list->num_rows; ++i) {
|
|
neworder[i] = (packet_list->rows[i])->pos;
|
|
(packet_list->rows[i])->pos = i;
|
|
}
|
|
|
|
path = gtk_tree_path_new();
|
|
|
|
gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
|
|
neworder);
|
|
|
|
gtk_tree_path_free(path);
|
|
g_free(neworder);
|
|
}
|
|
|
|
#endif /* NEW_PACKET_LIST */
|