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 470