1 /*
   2  *   ast-model.c 
   3  *
   4  *   A custom tree model to simplify viewing of AST objects.
   5  *   Modify from the Gtk+ tree view tutorial, custom-list.c
   6  *   by Tim-Philipp Mueller < tim at centricular dot net >
   7  *
   8  *   Copyright (C) 2010 Christopher Li
   9  */
  10 
  11 
  12 #include "ast-model.h"
  13 #include "stdint.h"
  14 
  15 /* boring declarations of local functions */
  16 
  17 static void ast_init(AstNode *pkg_tree);
  18 static void ast_class_init(AstNodeClass *klass);
  19 static void ast_tree_model_init(GtkTreeModelIface *iface);
  20 static void ast_finalize(GObject *object);
  21 static GtkTreeModelFlags ast_get_flags(GtkTreeModel *tree_model);
  22 static gint ast_get_n_columns(GtkTreeModel *tree_model);
  23 static GType ast_get_column_type(GtkTreeModel *tree_model, gint index);
  24 static gboolean ast_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
  25                                   GtkTreePath *path);
  26 static GtkTreePath *ast_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter); 
  27 static void ast_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
  28                                gint column, GValue *value);
  29 static gboolean ast_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter);
  30 static gboolean ast_iter_children(GtkTreeModel *tree_model,
  31                                        GtkTreeIter *iter,
  32                                        GtkTreeIter *parent);
  33 static gboolean ast_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter);
  34 static gint ast_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter);
  35 static gboolean ast_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
  36                                         GtkTreeIter *parent, gint n);
  37 static gboolean ast_iter_parent(GtkTreeModel *tree_model,
  38                                      GtkTreeIter *iter,
  39                                      GtkTreeIter *child);
  40 
  41 static GObjectClass *parent_class = NULL;  /* GObject stuff - nothing to worry about */
  42 
  43 static inline
  44 void inspect_child_node(AstNode *node)
  45 {
  46         if (node->inspect) {
  47                 node->inspect(node);
  48                 node->inspect = NULL;
  49         }
  50 }
  51 
  52 
  53 static inline
  54 AstNode* ast_nth_child(AstNode *node, int n)
  55 {
  56         if (!node)
  57                 return NULL;
  58 
  59         inspect_child_node(node);
  60 
  61         if (n >= node->childnodes->len)
  62                 return NULL;
  63         return g_array_index(node->childnodes, AstNode *, n);
  64 }
  65 
  66 
  67 static inline
  68 gboolean ast_set_iter(GtkTreeIter *iter, AstNode *node)
  69 {
  70         iter->user_data = node;
  71         iter->user_data2 = iter->user_data3 = NULL;
  72         return node != NULL;
  73 }
  74 
  75 
  76 /*****************************************************************************
  77  *
  78  *  ast_get_type: here we register our new type and its interfaces
  79  *                with the type system. If you want to implement
  80  *                additional interfaces like GtkTreeSortable, you
  81  *                will need to do it here.
  82  *
  83  *****************************************************************************/
  84 
  85 GType
  86 ast_get_type (void)
  87 {
  88         static GType ast_type = 0;
  89         static const GTypeInfo ast_info = {
  90                 sizeof (AstNodeClass),
  91                 NULL,                                         /* base_init */
  92                 NULL,                                         /* base_finalize */
  93                 (GClassInitFunc) ast_class_init,
  94                 NULL,                                         /* class finalize */
  95                 NULL,                                         /* class_data */
  96                 sizeof (AstNode),
  97                 0,                                           /* n_preallocs */
  98                 (GInstanceInitFunc) ast_init
  99         };
 100         static const GInterfaceInfo tree_model_info = {
 101                 (GInterfaceInitFunc) ast_tree_model_init,
 102                 NULL,
 103                 NULL
 104         };
 105 
 106 
 107 
 108         if (ast_type)
 109                 return ast_type;
 110 
 111         /* Some boilerplate type registration stuff */
 112         ast_type = g_type_register_static(G_TYPE_OBJECT, "AstNode",
 113                                                 &ast_info, (GTypeFlags)0);
 114 
 115         /* Here we register our GtkTreeModel interface with the type system */
 116         g_type_add_interface_static(ast_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
 117 
 118         return ast_type;
 119 }
 120 
 121 
 122 /*****************************************************************************
 123  *
 124  *  ast_class_init: more boilerplate GObject/GType stuff.
 125  *                  Init callback for the type system,
 126  *                  called once when our new class is created.
 127  *
 128  *****************************************************************************/
 129 
 130 static void
 131 ast_class_init (AstNodeClass *klass)
 132 {
 133         GObjectClass *object_class;
 134 
 135         parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
 136         object_class = (GObjectClass*) klass;
 137 
 138         object_class->finalize = ast_finalize;
 139 }
 140 
 141 /*****************************************************************************
 142  *
 143  *  ast_tree_model_init: init callback for the interface registration
 144  *                       in ast_get_type. Here we override
 145  *                       the GtkTreeModel interface functions that
 146  *                       we implement.
 147  *
 148  *****************************************************************************/
 149 
 150 static void
 151 ast_tree_model_init (GtkTreeModelIface *iface)
 152 {
 153         iface->get_flags       = ast_get_flags;
 154         iface->get_n_columns   = ast_get_n_columns;
 155         iface->get_column_type = ast_get_column_type;
 156         iface->get_iter        = ast_get_iter;
 157         iface->get_path        = ast_get_path;
 158         iface->get_value       = ast_get_value;
 159         iface->iter_next       = ast_iter_next;
 160         iface->iter_children   = ast_iter_children;
 161         iface->iter_has_child  = ast_iter_has_child;
 162         iface->iter_n_children = ast_iter_n_children;
 163         iface->iter_nth_child  = ast_iter_nth_child;
 164         iface->iter_parent     = ast_iter_parent;
 165 }
 166 
 167 
 168 /*****************************************************************************
 169  *
 170  *  ast_init: this is called every time a new ast node object
 171  *            instance is created (we do that in ast_new).
 172  *            Initialise the list structure's fields here.
 173  *
 174  *****************************************************************************/
 175 
 176 static void
 177 ast_init (AstNode *node)
 178 {
 179         node->childnodes = g_array_new(FALSE, TRUE, sizeof(AstNode *));
 180         node->stamp    = g_random_int(); /* Random int to check whether iters belong to out model */
 181 }
 182 
 183 
 184 /*****************************************************************************
 185  *
 186  *  ast_finalize: this is called just before an ast node is
 187  *                destroyed. Free dynamically allocated memory here.
 188  *
 189  *****************************************************************************/
 190 
 191 static void
 192 ast_finalize (GObject *object)
 193 {
 194         /*  AstNode *node = AST_NODE(object); */
 195 
 196         /* FIXME: free all node memory */
 197 
 198         /* must chain up - finalize parent */
 199         (* parent_class->finalize) (object);
 200 }
 201 
 202 
 203 /*****************************************************************************
 204  *
 205  *  ast_get_flags: tells the rest of the world whether our tree model
 206  *                 has any special characteristics. In our case,
 207  *                 we have a list model (instead of a tree), and each
 208  *                 tree iter is valid as long as the row in question
 209  *                 exists, as it only contains a pointer to our struct.
 210  *
 211  *****************************************************************************/
 212 
 213 static GtkTreeModelFlags
 214 ast_get_flags(GtkTreeModel *tree_model)
 215 {
 216         return (GTK_TREE_MODEL_ITERS_PERSIST);
 217 }
 218 
 219 
 220 /*****************************************************************************
 221  *
 222  *  ast_get_n_columns: tells the rest of the world how many data
 223  *                          columns we export via the tree model interface
 224  *
 225  *****************************************************************************/
 226 
 227 static gint
 228 ast_get_n_columns(GtkTreeModel *tree_model)
 229 {
 230         return 1;
 231 }
 232 
 233 
 234 /*****************************************************************************
 235  *
 236  *  ast_get_column_type: tells the rest of the world which type of
 237  *                       data an exported model column contains
 238  *
 239  *****************************************************************************/
 240 
 241 static GType
 242 ast_get_column_type(GtkTreeModel *tree_model,
 243                          gint index)
 244 {
 245         return G_TYPE_STRING;
 246 }
 247 
 248 
 249 /*****************************************************************************
 250  *
 251  *  ast_get_iter: converts a tree path (physical position) into a
 252  *                tree iter structure (the content of the iter
 253  *                fields will only be used internally by our model).
 254  *                We simply store a pointer to our AstNodeItem
 255  *                structure that represents that row in the tree iter.
 256  *
 257  *****************************************************************************/
 258 
 259 static gboolean
 260 ast_get_iter(GtkTreeModel *tree_model,
 261                   GtkTreeIter  *iter,
 262                   GtkTreePath  *path)
 263 {
 264         AstNode    *node;
 265         gint          *indices, depth;
 266         int i;
 267 
 268         node = AST_NODE(tree_model);
 269         indices = gtk_tree_path_get_indices(path);
 270         depth   = gtk_tree_path_get_depth(path);
 271 
 272         for (i = 0; i < depth; i++)
 273                 node = ast_nth_child(node, indices[i]);
 274 
 275         return ast_set_iter(iter, node);
 276 }
 277 
 278 
 279 /*****************************************************************************
 280  *
 281  *  ast_get_path: converts a tree iter into a tree path (ie. the
 282  *                physical position of that row in the list).
 283  *
 284  *****************************************************************************/
 285 
 286 static GtkTreePath *
 287 ast_get_path(GtkTreeModel *tree_model,
 288                   GtkTreeIter  *iter)
 289 {
 290         GtkTreePath  *path;
 291         AstNode   *root = AST_NODE(tree_model);
 292         AstNode   *node = AST_NODE(iter->user_data);
 293 
 294         path = gtk_tree_path_new();
 295         while (node != root) {
 296                 gtk_tree_path_prepend_index(path, node->index);
 297                 node = node->parent;
 298         }
 299         return path;
 300 }
 301 
 302 
 303 /*****************************************************************************
 304  *
 305  *  ast_get_value: Returns a row's exported data columns
 306  *                 (_get_value is what gtk_tree_model_get uses)
 307  *
 308  *****************************************************************************/
 309 
 310 static void
 311 ast_get_value(GtkTreeModel *tree_model,
 312                    GtkTreeIter  *iter,
 313                    gint          column,
 314                    GValue       *value)
 315 {
 316         AstNode    *node = iter->user_data;
 317 
 318         g_assert(AST_IS_NODE(tree_model));
 319         if (column != 1)
 320                 return;
 321 
 322         inspect_child_node(node);
 323 
 324         g_value_init(value, G_TYPE_STRING);
 325         g_value_set_string(value, node->text);
 326         return;
 327 }
 328 
 329 
 330 /*****************************************************************************
 331  *
 332  *  ast_iter_next: Takes an iter structure and sets it to point
 333  *                 to the next row.
 334  *
 335  *****************************************************************************/
 336 
 337 static gboolean
 338 ast_iter_next(GtkTreeModel  *tree_model,
 339                    GtkTreeIter   *iter)
 340 {
 341         AstNode    *node = iter->user_data;
 342         
 343         g_assert(AST_IS_NODE (tree_model));
 344 
 345         node = ast_nth_child(node->parent, node->index + 1);
 346         return ast_set_iter(iter, node);
 347 }
 348 
 349 
 350 /*****************************************************************************
 351  *
 352  *  ast_iter_children: Returns TRUE or FALSE depending on whether
 353  *                     the row specified by 'parent' has any children.
 354  *                     If it has children, then 'iter' is set to
 355  *                     point to the first child. Special case: if
 356  *                     'parent' is NULL, then the first top-level
 357  *                     row should be returned if it exists.
 358  *
 359  *****************************************************************************/
 360 
 361 static gboolean
 362 ast_iter_children(GtkTreeModel *tree_model,
 363                        GtkTreeIter  *iter,
 364                        GtkTreeIter  *parent)
 365 {
 366         return ast_iter_nth_child(tree_model, iter, parent, 0);
 367 }
 368 
 369 
 370 /*****************************************************************************
 371  *
 372  *  ast_iter_has_child: Returns TRUE or FALSE depending on whether
 373  *                      the row specified by 'iter' has any children.
 374  *                      We only have a list and thus no children.
 375  *
 376  *****************************************************************************/
 377 
 378 static gboolean
 379 ast_iter_has_child (GtkTreeModel *tree_model,
 380                          GtkTreeIter  *iter)
 381 {
 382         AstNode    *node = iter->user_data;
 383         inspect_child_node(node);
 384         return node->childnodes->len > 0;
 385 }
 386 
 387 
 388 /*****************************************************************************
 389  *
 390  *  ast_iter_n_children: Returns the number of children the row
 391  *                       specified by 'iter' has. This is usually 0,
 392  *                       as we only have a list and thus do not have
 393  *                       any children to any rows. A special case is
 394  *                       when 'iter' is NULL, in which case we need
 395  *                       to return the number of top-level node,
 396  *                       ie. the number of rows in our list.
 397  *
 398  *****************************************************************************/
 399 
 400 static gint
 401 ast_iter_n_children (GtkTreeModel *tree_model,
 402                           GtkTreeIter  *iter)
 403 {
 404         AstNode  *node = iter ? iter->user_data
 405                                 : AST_NODE(tree_model);
 406 
 407         inspect_child_node(node);
 408         return node->childnodes->len;
 409 }
 410 
 411 
 412 /*****************************************************************************
 413  *
 414  *  ast_iter_nth_child: If the row specified by 'parent' has any
 415  *                      children, set 'iter' to the n-th child and
 416  *                      return TRUE if it exists, otherwise FALSE.
 417  *                      A special case is when 'parent' is NULL, in
 418  *                      which case we need to set 'iter' to the n-th
 419  *                      row if it exists.
 420  *
 421  *****************************************************************************/
 422 
 423 static gboolean
 424 ast_iter_nth_child(GtkTreeModel *tree_model,
 425                         GtkTreeIter  *iter,
 426                         GtkTreeIter  *parent,
 427                         gint          n)
 428 {
 429         AstNode    *node = parent ? parent->user_data : (AstNode*) tree_model;
 430         GArray *array = node->childnodes;
 431         if (n >= array->len)
 432                 return FALSE;
 433         iter->user_data = g_array_index(array, AstNode *, n);
 434         return TRUE;
 435 }
 436 
 437 
 438 /*****************************************************************************
 439  *
 440  *  ast_iter_parent: Point 'iter' to the parent node of 'child'. As
 441  *                   we have a list and thus no children and no
 442  *                   parents of children, we can just return FALSE.
 443  *
 444  *****************************************************************************/
 445 
 446 static gboolean
 447 ast_iter_parent (GtkTreeModel *tree_model,
 448                       GtkTreeIter  *iter,
 449                       GtkTreeIter  *child)
 450 {
 451         AstNode *node = (AstNode *) child->user_data;
 452         iter->user_data = node->parent;
 453         return node->parent != NULL;
 454 }
 455 
 456 
 457 AstNode *
 458 ast_new (AstNode *parent, int index, const char *text, void *ptr, void (*inspect)(AstNode*))
 459 {
 460         AstNode *node = (AstNode*) g_object_new (AST_TYPE_NODE, NULL);
 461         g_assert(node != NULL);
 462         node->parent = parent;
 463         node->index = index;
 464         node->text = text;
 465         node->inspect = inspect;
 466         node->ptr = ptr;
 467         return node;
 468 }
 469