wireshark/ui/gtk/proto_tree_model.c

478 lines
11 KiB
C

/* proto_tree_model.c
*
* $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 was originally based on the GTK+ Tree View tutorial at
* http://scentric.net/tutorial */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <epan/addr_resolv.h>
#include <epan/proto.h>
static GObjectClass *parent_class = NULL;
typedef struct {
GObjectClass parent_class;
} ProtoTreeModelClass;
struct proto_tree_model {
GObject parent; /** MUST be first */
/** Random integer to check whether an iter belongs to our model. */
gint stamp;
proto_tree *protocol_tree;
int with_hidden;
gboolean resolv_forced;
e_addr_resolve resolv_flags;
};
#include "proto_tree_model.h"
static GtkTreeModelFlags
proto_tree_model_get_flags(GtkTreeModel *tree_model)
{
g_return_val_if_fail(PROTO_IS_TREE(tree_model), (GtkTreeModelFlags)0);
return GTK_TREE_MODEL_ITERS_PERSIST;
}
static gint
proto_tree_model_get_n_columns(GtkTreeModel *tree_model)
{
g_return_val_if_fail(PROTO_IS_TREE(tree_model), 0);
return 2;
}
static GType
proto_tree_model_get_column_type(GtkTreeModel *tree_model, gint idx)
{
g_return_val_if_fail(PROTO_IS_TREE(tree_model), G_TYPE_INVALID);
g_return_val_if_fail(idx == 0 || idx == 1, G_TYPE_INVALID);
switch (idx) {
case 0:
return G_TYPE_STRING;
case 1:
return G_TYPE_POINTER;
}
/* never here */
return G_TYPE_INVALID;
}
static gboolean
proto_tree_model_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n)
{
ProtoTreeModel *model;
proto_node *node;
g_return_val_if_fail(PROTO_IS_TREE(tree_model), FALSE);
model = (ProtoTreeModel *) tree_model;
if (parent) {
g_return_val_if_fail(parent->stamp == model->stamp, FALSE);
node = parent->user_data;
} else
node = model->protocol_tree;
if (!node)
return FALSE;
node = node->first_child;
while (node != NULL) {
if (model->with_hidden || !PROTO_ITEM_IS_HIDDEN(node)) {
if (!n)
break;
n--;
}
node = node->next;
}
/* not found? */
if (!node)
return FALSE;
iter->stamp = model->stamp;
iter->user_data = node;
return TRUE;
}
static gboolean
proto_tree_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
{
gint *indices, depth;
g_assert(PROTO_IS_TREE(tree_model));
g_assert(path != NULL);
indices = gtk_tree_path_get_indices(path);
depth = gtk_tree_path_get_depth(path);
g_return_val_if_fail(depth > 0, FALSE);
if (!proto_tree_model_iter_nth_child(tree_model, iter, NULL, indices[0]))
return FALSE;
while (--depth) {
indices++;
if (!proto_tree_model_iter_nth_child(tree_model, iter, iter, *indices))
return FALSE;
}
return TRUE;
}
static char *
fi_get_string(field_info *fi)
{
gchar label_str[ITEM_LABEL_LENGTH];
gchar *label_ptr;
if (!fi->rep) {
label_ptr = label_str;
proto_item_fill_label(fi, label_str);
} else
label_ptr = fi->rep->representation;
if (FI_GET_FLAG(fi, FI_GENERATED)) {
if (FI_GET_FLAG(fi, FI_HIDDEN))
label_ptr = g_strdup_printf("<[%s]>", label_ptr);
else
label_ptr = g_strdup_printf("[%s]", label_ptr);
} else if (FI_GET_FLAG(fi, FI_HIDDEN))
label_ptr = g_strdup_printf("<%s>", label_ptr);
else
label_ptr = g_strdup(label_ptr);
return label_ptr;
}
static void
proto_tree_model_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value)
{
ProtoTreeModel *model;
proto_node *node;
field_info *fi;
g_return_if_fail(PROTO_IS_TREE(tree_model));
model = (ProtoTreeModel *) tree_model;
g_return_if_fail(iter != NULL);
g_return_if_fail(iter->stamp == model->stamp);
g_return_if_fail(column == 0 || column == 1);
node = iter->user_data;
fi = PNODE_FINFO(node);
g_assert(fi && "dissection with an invisible proto tree?");
switch (column) {
case 0:
{
g_value_init(value, G_TYPE_STRING);
if (model->resolv_forced) {
e_addr_resolve old_flags = gbl_resolv_flags;
gbl_resolv_flags = model->resolv_flags;
g_value_take_string(value, fi_get_string(fi));
gbl_resolv_flags = old_flags;
} else
g_value_take_string(value, fi_get_string(fi));
break;
}
case 1:
g_value_init(value, G_TYPE_POINTER);
g_value_set_pointer(value, fi);
break;
}
}
static gboolean
proto_tree_model_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
{
ProtoTreeModel *model;
proto_node *current;
g_return_val_if_fail(PROTO_IS_TREE(tree_model), FALSE);
model = (ProtoTreeModel *) tree_model;
g_return_val_if_fail(iter->stamp == model->stamp, FALSE);
current = iter->user_data;
current = current->next;
while (current) {
if (model->with_hidden || !PROTO_ITEM_IS_HIDDEN(current)) {
iter->user_data = current;
return TRUE;
}
current = current->next;
}
return FALSE;
}
static gboolean
proto_tree_model_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent)
{
return proto_tree_model_iter_nth_child(tree_model, iter, parent, 0);
}
static gint
proto_tree_model_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
{
ProtoTreeModel *model;
proto_node *node;
gint count;
g_return_val_if_fail(PROTO_IS_TREE(tree_model), 0);
model = (ProtoTreeModel *) tree_model;
g_return_val_if_fail(iter == NULL || iter->user_data != NULL, 0);
if (iter) {
g_return_val_if_fail(iter->stamp == model->stamp, 0);
node = iter->user_data;
} else
node = model->protocol_tree;
if (!node)
return 0;
count = 0;
node = node->first_child;
while (node != NULL) {
if (model->with_hidden || !PROTO_ITEM_IS_HIDDEN(node))
count++;
node = node->next;
}
return count;
}
static GtkTreePath *
proto_tree_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
{
ProtoTreeModel *model;
GtkTreePath *path;
proto_node *node;
g_return_val_if_fail(PROTO_IS_TREE(tree_model), NULL);
model = (ProtoTreeModel *) tree_model;
g_return_val_if_fail(iter != NULL, NULL);
g_return_val_if_fail(iter->stamp == model->stamp, NULL);
node = iter->user_data;
g_return_val_if_fail(node != model->protocol_tree, NULL);
path = gtk_tree_path_new();
do {
proto_node *cur = node;
proto_node *node_i;
int pos;
node = node->parent;
pos = 0;
for (node_i = node->first_child; node_i; node_i = node_i->next) {
if (model->with_hidden || !PROTO_ITEM_IS_HIDDEN(node_i)) {
if (node_i == cur)
break;
pos++;
}
}
g_assert(node_i != NULL);
gtk_tree_path_prepend_index(path, pos);
} while (node != model->protocol_tree);
return path;
}
static gboolean
proto_tree_model_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter)
{
/* optimized version of:
* return proto_tree_model_iter_n_children(tree_model, iter) != 0;
* synchronize when changed!
*/
ProtoTreeModel *model;
proto_node *node;
g_return_val_if_fail(PROTO_IS_TREE(tree_model), FALSE);
model = (ProtoTreeModel *) tree_model;
g_return_val_if_fail(iter == NULL || iter->user_data != NULL, FALSE);
if (iter) {
g_return_val_if_fail(iter->stamp == model->stamp, FALSE);
node = iter->user_data;
} else
node = model->protocol_tree;
if (!node)
return FALSE;
node = node->first_child;
while (node != NULL) {
if (model->with_hidden || !PROTO_ITEM_IS_HIDDEN(node))
return TRUE;
node = node->next;
}
return FALSE;
}
static gboolean
proto_tree_model_iter_parent(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child)
{
ProtoTreeModel *model;
proto_node *node;
g_return_val_if_fail(PROTO_IS_TREE(tree_model), FALSE);
model = (ProtoTreeModel *) tree_model;
g_return_val_if_fail(iter != NULL, FALSE);
g_return_val_if_fail(child->stamp == model->stamp, FALSE);
node = child->user_data;
if (node->parent == model->protocol_tree)
return FALSE;
g_return_val_if_fail(node->parent != NULL, FALSE);
iter->stamp = model->stamp;
iter->user_data = node->parent;
return TRUE;
}
static void
proto_tree_model_tree_init(GtkTreeModelIface *iface)
{
iface->get_flags = proto_tree_model_get_flags;
iface->get_n_columns = proto_tree_model_get_n_columns;
iface->get_column_type = proto_tree_model_get_column_type;
iface->get_iter = proto_tree_model_get_iter;
iface->get_path = proto_tree_model_get_path;
iface->get_value = proto_tree_model_get_value;
iface->iter_next = proto_tree_model_iter_next;
iface->iter_children = proto_tree_model_iter_children;
iface->iter_has_child = proto_tree_model_iter_has_child;
iface->iter_n_children = proto_tree_model_iter_n_children;
iface->iter_nth_child = proto_tree_model_iter_nth_child;
iface->iter_parent = proto_tree_model_iter_parent;
}
static void
proto_tree_model_init(ProtoTreeModel *model)
{
/* To check whether an iter belongs to our model. */
model->stamp = g_random_int();
}
static void
_class_finalize(GObject *object)
{
/* must chain up - finalize parent */
(*parent_class->finalize)(object);
}
static void
proto_tree_model_class_init(ProtoTreeModelClass *klass)
{
GObjectClass *object_class;
parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
object_class = (GObjectClass*) klass;
object_class->finalize = _class_finalize;
}
GType
proto_tree_model_get_type(void)
{
static GType proto_tree_type = 0;
if (proto_tree_type == 0) {
static const GTypeInfo proto_tree_info = {
sizeof(ProtoTreeModelClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) proto_tree_model_class_init,
NULL, /* class finalize */
NULL, /* class_data */
sizeof(ProtoTreeModel),
0, /* n_preallocs */
(GInstanceInitFunc) proto_tree_model_init,
NULL /* value_table */
};
static const GInterfaceInfo tree_model_info = {
(GInterfaceInitFunc) proto_tree_model_tree_init,
NULL,
NULL
};
/* Register the new derived type with the GObject type system */
proto_tree_type = g_type_register_static(G_TYPE_OBJECT,
"ProtoTreeModel",
&proto_tree_info,
(GTypeFlags)0);
g_type_add_interface_static(proto_tree_type,
GTK_TYPE_TREE_MODEL,
&tree_model_info);
}
return proto_tree_type;
}
void
proto_tree_model_force_resolv(ProtoTreeModel *model, const e_addr_resolve *flags)
{
model->resolv_forced = TRUE;
model->resolv_flags = *flags;
}
ProtoTreeModel *
proto_tree_model_new(proto_tree *protocol_tree, int display_hidden_proto_items)
{
ProtoTreeModel *model;
model = (ProtoTreeModel *) g_object_new(PROTO_TYPE_TREE, NULL);
g_assert(model != NULL);
model->protocol_tree = protocol_tree;
model->with_hidden = display_hidden_proto_items;
model->resolv_forced = FALSE;
return model;
}