]> Pileus Git - ~andy/gtk/blob - gtk/gtktypeutils.c
wrapped enum definitions with glib macros.
[~andy/gtk] / gtk / gtktypeutils.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include <string.h>
20 #include "gtkobject.h"
21 #include "gtktypeutils.h"
22
23
24 #define TYPE_NODES_BLOCK_SIZE   (200)
25
26 typedef struct _GtkTypeNode GtkTypeNode;
27
28 struct _GtkTypeNode
29 {
30   GtkType type;
31   GtkTypeInfo type_info;
32   guint n_supers : 24;
33   guint chunk_alloc_locked : 1;
34   GtkType *supers;
35   GtkType parent_type;
36   gpointer klass;
37   GList *children_types;
38   GMemChunk *mem_chunk;
39 };
40
41
42 #define LOOKUP_TYPE_NODE(node_var, type)        { \
43   if (type > 0) \
44   { \
45     register GtkType sqn = GTK_TYPE_SEQNO (type); \
46     if (sqn < n_type_nodes) \
47       node_var = type_nodes + sqn; \
48     else \
49       node_var = NULL; \
50   } \
51   else \
52     node_var = NULL; \
53 }
54
55 static void  gtk_type_class_init                (GtkTypeNode *node);
56 static guint gtk_type_name_hash                 (const char  *key);
57 static gint  gtk_type_name_compare              (const char  *a,
58                                                  const char  *b);
59 static void  gtk_type_init_builtin_types        (void);
60
61 static GtkTypeNode *type_nodes = NULL;
62 static guint        n_type_nodes = 0;
63 static GHashTable  *type_name_2_type_ht = NULL;
64 static const gchar *key_enum_vals = "gtk-type-enum-values";
65 static guint        key_id_enum_vals = 0;
66
67
68 static GtkTypeNode*
69 gtk_type_node_next_and_invalidate (void)
70 {
71   static guint  n_free_type_nodes = 0;
72   register GtkTypeNode  *node;
73   register GtkType new_type;
74
75   /* don't keep *any* GtkTypeNode pointers across invokation of this function!!!
76    */
77
78   if (n_free_type_nodes == 0)
79     {
80       register guint i;
81       register guint size;
82       
83       /* nearest pow
84        */
85       size = n_type_nodes + TYPE_NODES_BLOCK_SIZE;
86       size *= sizeof (GtkTypeNode);
87       i = 1;
88       while (i < size)
89         i <<= 1;
90       size = i;
91       
92       type_nodes = g_realloc (type_nodes, size);
93
94       n_free_type_nodes = size / sizeof (GtkTypeNode) - n_type_nodes;
95       
96       memset (type_nodes + n_type_nodes, 0, n_free_type_nodes * sizeof (GtkTypeNode));
97     }
98
99   new_type = n_type_nodes++;
100   n_free_type_nodes--;
101
102   LOOKUP_TYPE_NODE (node, new_type);
103   if (node)
104     node->type = new_type;
105
106   return node;
107 }
108
109 void
110 gtk_type_init (void)
111 {
112   if (n_type_nodes == 0)
113     {
114       GtkTypeNode *zero;
115
116       g_assert (sizeof (GtkType) >= 4);
117
118       zero = gtk_type_node_next_and_invalidate ();
119       g_assert (zero == NULL);
120
121       type_name_2_type_ht = g_hash_table_new ((GHashFunc) gtk_type_name_hash,
122                                               (GCompareFunc) gtk_type_name_compare);
123
124       gtk_type_init_builtin_types ();
125     }
126 }
127
128 void
129 gtk_type_set_chunk_alloc (GtkType      type,
130                           guint        n_chunks)
131 {
132   GtkTypeNode *node;
133
134   LOOKUP_TYPE_NODE (node, type);
135   g_return_if_fail (node != NULL);
136   g_return_if_fail (node->chunk_alloc_locked == FALSE);
137
138   if (node->mem_chunk)
139     {
140       g_mem_chunk_destroy (node->mem_chunk);
141       node->mem_chunk = NULL;
142     }
143
144   if (n_chunks)
145     node->mem_chunk = g_mem_chunk_new (node->type_info.type_name,
146                                        node->type_info.object_size,
147                                        node->type_info.object_size * n_chunks,
148                                        G_ALLOC_AND_FREE);
149 }
150
151 GtkType
152 gtk_type_unique (GtkType      parent_type,
153                  GtkTypeInfo *type_info)
154 {
155   GtkTypeNode *new_node;
156   GtkTypeNode *parent;
157   guint i;
158
159   g_return_val_if_fail (type_info != NULL, 0);
160
161   if (n_type_nodes == 0)
162     gtk_type_init ();
163
164   if (g_hash_table_lookup (type_name_2_type_ht, type_info->type_name))
165     {
166       g_warning ("gtk_type_unique(): type `%s' already exists.", type_info->type_name);
167       return 0;
168     }
169   if (parent_type)
170     {
171       GtkTypeNode *tmp_node;
172
173       LOOKUP_TYPE_NODE (tmp_node, parent_type);
174       if (!tmp_node)
175         {
176           g_warning ("gtk_type_unique(): unknown parent type `%u'.", parent_type);
177           return 0;
178         }
179     }
180
181   /* relookup pointer afterwards.
182    */
183   new_node = gtk_type_node_next_and_invalidate ();
184
185   if (parent_type)
186     {
187       new_node->type = GTK_TYPE_MAKE (parent_type, new_node->type);
188       LOOKUP_TYPE_NODE (parent, parent_type);
189     }
190   else
191     {
192       g_assert (new_node->type <= 0xff);
193       parent = NULL;
194     }
195
196   new_node->type_info = *type_info;
197   new_node->type_info.type_name = g_strdup (type_info->type_name);
198   new_node->n_supers = parent ? parent->n_supers + 1 : 0;
199   new_node->chunk_alloc_locked = FALSE;
200   new_node->supers = g_new0 (GtkType, new_node->n_supers + 1);
201   new_node->parent_type = parent_type;
202   new_node->klass = NULL;
203   new_node->children_types = NULL;
204   new_node->mem_chunk = NULL;
205
206   if (parent)
207     parent->children_types = g_list_append (parent->children_types, GUINT_TO_POINTER (new_node->type));
208
209   parent = new_node;
210   for (i = 0; i < new_node->n_supers + 1; i++)
211     {
212       new_node->supers[i] = parent->type;
213       LOOKUP_TYPE_NODE (parent, parent->parent_type);
214     }
215     
216   g_hash_table_insert (type_name_2_type_ht, new_node->type_info.type_name, GUINT_TO_POINTER (new_node->type));
217
218   return new_node->type;
219 }
220
221 gchar*
222 gtk_type_name (GtkType type)
223 {
224   GtkTypeNode *node;
225
226   LOOKUP_TYPE_NODE (node, type);
227
228   if (node)
229     return node->type_info.type_name;
230
231   return NULL;
232 }
233
234 GtkType
235 gtk_type_from_name (const gchar *name)
236 {
237   if (type_name_2_type_ht)
238     {
239       GtkType type;
240       
241       type = GPOINTER_TO_UINT (g_hash_table_lookup (type_name_2_type_ht, (gpointer) name));
242
243       return type;
244     }
245
246   return 0;
247 }
248
249 GtkType
250 gtk_type_parent (GtkType type)
251 {
252   GtkTypeNode *node;
253
254   LOOKUP_TYPE_NODE (node, type);
255   if (node)
256     return node->parent_type;
257
258   return 0;
259 }
260
261 gpointer
262 gtk_type_parent_class (GtkType type)
263 {
264   GtkTypeNode *node;
265
266   LOOKUP_TYPE_NODE (node, type);
267   g_return_val_if_fail (node != NULL, NULL);
268
269   if (node)
270     {
271       LOOKUP_TYPE_NODE (node, node->parent_type);
272
273       if (node)
274         {
275           if (!node->klass)
276             gtk_type_class_init (node);
277
278           return node->klass;
279         }
280     }
281
282   return NULL;
283 }
284
285 gpointer
286 gtk_type_class (GtkType type)
287 {
288   GtkTypeNode *node;
289
290   LOOKUP_TYPE_NODE (node, type);
291   g_return_val_if_fail (node != NULL, NULL);
292
293   if (!node->klass)
294     gtk_type_class_init (node);
295
296   return node->klass;
297 }
298
299 gpointer
300 gtk_type_new (GtkType type)
301 {
302   GtkTypeNode *node;
303   GtkObject *object;
304   gpointer klass;
305   guint i;
306
307   LOOKUP_TYPE_NODE (node, type);
308   g_return_val_if_fail (node != NULL, NULL);
309
310   klass = gtk_type_class (type);
311   node->chunk_alloc_locked = TRUE;
312   if (node->mem_chunk)
313     {
314       object = g_mem_chunk_alloc (node->mem_chunk);
315       memset (object, 0, node->type_info.object_size);
316     }
317   else
318     object = g_malloc0 (node->type_info.object_size);
319   object->klass = klass;
320
321   for (i = node->n_supers; i > 0; i--)
322     {
323       GtkTypeNode *pnode;
324
325       LOOKUP_TYPE_NODE (pnode, node->supers[i]);
326       if (pnode->type_info.object_init_func)
327         (* pnode->type_info.object_init_func) (object);
328     }
329   if (node->type_info.object_init_func)
330     (* node->type_info.object_init_func) (object);
331
332   return object;
333 }
334
335 void
336 gtk_type_free (GtkType      type,
337                gpointer     mem)
338 {
339   GtkTypeNode *node;
340
341   g_return_if_fail (mem != NULL);
342   LOOKUP_TYPE_NODE (node, type);
343   g_return_if_fail (node != NULL);
344
345   if (node->mem_chunk)
346     g_mem_chunk_free (node->mem_chunk, mem);
347   else
348     g_free (mem);
349 }
350
351 void
352 gtk_type_describe_heritage (GtkType type)
353 {
354   GtkTypeNode *node;
355   gint first;
356
357   LOOKUP_TYPE_NODE (node, type);
358   first = TRUE;
359
360   while (node)
361     {
362       if (first)
363         {
364           first = FALSE;
365           g_print ("is a ");
366         }
367
368       if (node->type_info.type_name)
369         g_print ("%s\n", node->type_info.type_name);
370       else
371         g_print ("<unnamed type>\n");
372
373       LOOKUP_TYPE_NODE (node, node->parent_type);
374     }
375 }
376
377 void
378 gtk_type_describe_tree (GtkType type,
379                         gint  show_size)
380 {
381   GtkTypeNode *node;
382   
383   LOOKUP_TYPE_NODE (node, type);
384   
385   if (node)
386     {
387       static gint indent = 0;
388       GList *list;
389       guint old_indent;
390       guint i;
391       
392       for (i = 0; i < indent; i++)
393         g_print (" ");
394       
395       if (node->type_info.type_name)
396         g_print ("%s", node->type_info.type_name);
397       else
398         g_print ("(no-name)");
399       
400       if (show_size)
401         g_print (" ( %d bytes )\n", node->type_info.object_size);
402       else
403         g_print ("\n");
404       
405       old_indent = indent;
406       indent += 4;
407       
408       for (list = node->children_types; list; list = list->next)
409         gtk_type_describe_tree (GPOINTER_TO_UINT (list->data), show_size);
410       
411       indent = old_indent;
412     }
413 }
414
415 gint
416 gtk_type_is_a (GtkType type,
417                GtkType is_a_type)
418 {
419   if (type == is_a_type)
420     return TRUE;
421   else
422     {
423       register GtkTypeNode *node;
424
425       LOOKUP_TYPE_NODE (node, type);
426       if (node)
427         {
428           register GtkTypeNode *a_node;
429           
430           LOOKUP_TYPE_NODE (a_node, is_a_type);
431           if (a_node)
432             {
433               if (a_node->n_supers <= node->n_supers)
434                 return node->supers[node->n_supers - a_node->n_supers] == is_a_type;
435             }
436         }
437     }
438   
439   return FALSE;
440 }
441
442 void
443 gtk_type_get_arg (GtkObject   *object,
444                   GtkType      type,
445                   GtkArg      *arg,
446                   guint        arg_id)
447 {
448   GtkTypeNode *node;
449
450   g_return_if_fail (object != NULL);
451   g_return_if_fail (arg != NULL);
452
453   LOOKUP_TYPE_NODE (node, type);
454
455   if (node && node->type_info.arg_get_func)
456     (* node->type_info.arg_get_func) (object, arg, arg_id);
457   else
458     arg->type = GTK_TYPE_INVALID;
459 }
460
461 void
462 gtk_type_set_arg (GtkObject *object,
463                   GtkType    type,
464                   GtkArg    *arg,
465                   guint      arg_id)
466 {
467   GtkTypeNode *node;
468
469   g_return_if_fail (object != NULL);
470   g_return_if_fail (arg != NULL);
471
472   LOOKUP_TYPE_NODE (node, type);
473
474   if (node && node->type_info.arg_set_func)
475     (* node->type_info.arg_set_func) (object, arg, arg_id);
476 }
477
478 GtkArg*
479 gtk_arg_copy (GtkArg         *src_arg,
480               GtkArg         *dest_arg)
481 {
482   g_return_val_if_fail (src_arg != NULL, NULL);
483
484   if (!dest_arg)
485     {
486       dest_arg = g_new0 (GtkArg, 1);
487       dest_arg->name = src_arg->name;
488     }
489
490   dest_arg->type = src_arg->type;
491   dest_arg->d = src_arg->d;
492
493   if (src_arg->type == GTK_TYPE_STRING)
494     dest_arg->d.string_data = g_strdup (src_arg->d.string_data);
495
496   return dest_arg;
497 }
498
499 GtkEnumValue*
500 gtk_enum_get_values (GtkType      enum_type)
501 {
502   if (gtk_type_is_a (enum_type, GTK_TYPE_ENUM) ||
503       gtk_type_is_a (enum_type, GTK_TYPE_FLAGS))
504     return g_dataset_id_get_data (gtk_type_name (enum_type), key_id_enum_vals);
505
506   g_warning ("gtk_enum_get_values(): type `%s' is not derived from `enum' or `flags'",
507              gtk_type_name (enum_type));
508
509   return NULL;
510 }
511
512 void
513 gtk_enum_set_values (GtkType       enum_type,
514                      GtkEnumValue *values)
515 {
516   if (!key_id_enum_vals)
517     key_id_enum_vals = g_dataset_force_id (key_enum_vals);
518     
519   if (gtk_type_is_a (enum_type, GTK_TYPE_ENUM) ||
520       gtk_type_is_a (enum_type, GTK_TYPE_FLAGS))
521     {
522       gchar *type_name;
523
524       type_name = gtk_type_name (enum_type);
525       if (g_dataset_id_get_data (type_name, key_id_enum_vals))
526         g_warning ("gtk_enum_set_values(): enum values for `%s' are already set",
527                    type_name);
528       else
529         g_dataset_id_set_data (type_name,
530                                key_id_enum_vals,
531                                values);
532       return;
533     }
534
535   g_warning ("gtk_enum_set_values(): type `%s' is not derived from `enum' or `flags'",
536              gtk_type_name (enum_type));
537 }
538
539 static void
540 gtk_type_class_init (GtkTypeNode *node)
541 {
542   if (!node->klass && node->type_info.class_size)
543     {
544       node->klass = g_malloc0 (node->type_info.class_size);
545
546       if (node->parent_type)
547         {
548           GtkTypeNode *parent;
549
550           LOOKUP_TYPE_NODE (parent, node->parent_type);
551           if (!parent->klass)
552             gtk_type_class_init (parent);
553
554           if (parent->klass)
555             memcpy (node->klass, parent->klass, parent->type_info.class_size);
556         }
557
558       if (gtk_type_is_a (node->type, GTK_TYPE_OBJECT))
559         {
560           GtkObjectClass *object_class;
561
562           /* FIXME: this initialization needs to be done through
563            * a function pointer someday.
564            */
565           g_assert (node->type_info.class_size >= sizeof (GtkObjectClass));
566           
567           object_class = node->klass;
568           object_class->type = node->type;
569           object_class->signals = NULL;
570           object_class->nsignals = 0;
571           object_class->n_args = 0;
572         }
573       
574       if (node->type_info.class_init_func)
575         (* node->type_info.class_init_func) (node->klass);
576     }
577 }
578
579 static guint
580 gtk_type_name_hash (const char *key)
581 {
582   guint result;
583
584   result = 0;
585   while (*key)
586     result += (result << 3) + *key++;
587
588   return result;
589 }
590
591 static gint
592 gtk_type_name_compare (const char *a,
593                        const char *b)
594 {
595   return (strcmp (a, b) == 0);
596 }
597
598 static GtkType
599 gtk_type_register_builtin (char   *name,
600                            GtkType parent)
601 {
602   GtkTypeInfo info;
603
604   info.type_name = name;
605   info.object_size = info.class_size = 0;
606   info.class_init_func = NULL;
607   info.object_init_func = NULL;
608   info.arg_set_func = NULL;
609   info.arg_get_func = NULL;
610
611   return gtk_type_unique (parent, &info);
612 }
613
614 extern void gtk_object_init_type (void);
615
616 GtkType gtk_type_builtins[GTK_TYPE_NUM_BUILTINS];
617
618 #include "makeenums.h"          /* include for various places with enum definitions */
619 #include "gtktypebuiltins1.c"
620
621 static void
622 gtk_type_init_builtin_types (void)
623 {
624   /* GTK_TYPE_INVALID has typeid 0.  The first type id returned by
625    * gtk_type_unique is 1, which is GTK_TYPE_NONE.  And so on.
626    */
627
628   struct {
629     GtkType type_id;
630     gchar *name;
631   } fundamental_info[] = {
632     { GTK_TYPE_NONE,            "void" },
633     { GTK_TYPE_CHAR,            "char" },
634     { GTK_TYPE_BOOL,            "bool" },
635     { GTK_TYPE_INT,             "int" },
636     { GTK_TYPE_UINT,            "uint" },
637     { GTK_TYPE_LONG,            "long" },
638     { GTK_TYPE_ULONG,           "ulong" },
639     { GTK_TYPE_FLOAT,           "float" },
640     { GTK_TYPE_DOUBLE,          "double" },
641     { GTK_TYPE_STRING,          "string" },
642     { GTK_TYPE_ENUM,            "enum" },
643     { GTK_TYPE_FLAGS,           "flags" },
644     { GTK_TYPE_BOXED,           "boxed" },
645     { GTK_TYPE_FOREIGN,         "foreign" },
646     { GTK_TYPE_CALLBACK,        "callback" },
647     { GTK_TYPE_ARGS,            "args" },
648     
649     { GTK_TYPE_POINTER,         "pointer" },
650     { GTK_TYPE_SIGNAL,          "signal" },
651     { GTK_TYPE_C_CALLBACK,      "c_callback" }
652   };
653   struct {
654     gchar *name;
655     GtkType parent;
656     GtkEnumValue *values;
657   } builtin_info[] = {
658 #include "gtktypebuiltins2.c"
659     { NULL }
660   };
661   guint i;
662   
663   for (i = 0; i < sizeof (fundamental_info) / sizeof (fundamental_info[0]); i++)
664     {
665       GtkType id;
666
667       id = gtk_type_register_builtin (fundamental_info[i].name,
668                                       GTK_TYPE_INVALID);
669       g_assert (id == fundamental_info[i].type_id);
670     }
671   
672   gtk_object_init_type ();
673   
674   for (i = 0; builtin_info[i].name; i++)
675     {
676       gtk_type_builtins[i] =
677         gtk_type_register_builtin (builtin_info[i].name,
678                                    builtin_info[i].parent);
679       if (gtk_type_is_a (gtk_type_builtins[i], GTK_TYPE_ENUM) ||
680           gtk_type_is_a (gtk_type_builtins[i], GTK_TYPE_FLAGS))
681         gtk_enum_set_values (gtk_type_builtins[i], builtin_info[i].values);
682     }
683 }