aboutsummaryrefslogtreecommitdiffstats
path: root/ui/gtk/proto_tree_model.c
diff options
context:
space:
mode:
authorJakub Zawadzki <darkjames-ws@darkjames.pl>2012-06-10 22:07:54 +0000
committerJakub Zawadzki <darkjames-ws@darkjames.pl>2012-06-10 22:07:54 +0000
commitf372010ee83aa3438f633d38c7d8e46516c1ab7b (patch)
treed72f197e9919300f5671b1fb734144c8c03639f8 /ui/gtk/proto_tree_model.c
parenta2bb94c3b33d53f42534aceb7cc67aab1d1fb1f9 (diff)
Custom model for packet details
Original patch tracked in bug #7224 svn path=/trunk/; revision=43189
Diffstat (limited to 'ui/gtk/proto_tree_model.c')
-rw-r--r--ui/gtk/proto_tree_model.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/ui/gtk/proto_tree_model.c b/ui/gtk/proto_tree_model.c
new file mode 100644
index 0000000000..66ecc0aacf
--- /dev/null
+++ b/ui/gtk/proto_tree_model.c
@@ -0,0 +1,455 @@
+/* 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/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;
+};
+
+#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);
+ 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;
+}
+
+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;
+
+ return model;
+}