nexmon – Rev 1

Subversion Repositories:
Rev:
/* proto_tree_model.c
 *
 * 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 */

#include "config.h"

#include <string.h>

#include <gtk/gtk.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 = (proto_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 = (proto_node *)iter->user_data;
        fi = PNODE_FINFO(node);

        /* dissection with an invisible proto tree? */
        g_assert(fi);

        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 = (proto_node *)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 = (proto_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 = (proto_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 = (proto_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 = (proto_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;
}

/*
 * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */