]> Pileus Git - ~andy/gtk/blob - gtk/gtktypeutils.c
asure that the type system has been initialized.
[~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;
33   GtkType *supers;
34   GtkType parent_type;
35   gpointer klass;
36   GList *children_types;
37 };
38
39
40 #define LOOKUP_TYPE_NODE(node_var, type)        { \
41   if (type > 0) \
42   { \
43     register GtkType sqn = GTK_TYPE_SEQNO (type); \
44     if (sqn < n_type_nodes) \
45       node_var = type_nodes + sqn; \
46     else \
47       node_var = NULL; \
48   } \
49   else \
50     node_var = NULL; \
51 }
52
53 static void  gtk_type_class_init                (GtkTypeNode *node);
54 static guint gtk_type_name_hash                 (const char  *key);
55 static gint  gtk_type_name_compare              (const char  *a,
56                                                  const char  *b);
57 static void  gtk_type_init_builtin_types        (void);
58
59 static GtkTypeNode *type_nodes = NULL;
60 static guint        n_type_nodes = 0;
61 static GHashTable *type_name_2_type_ht = NULL;
62
63
64 static GtkTypeNode*
65 gtk_type_node_next_and_invalidate (void)
66 {
67   static guint  n_free_type_nodes = 0;
68   register GtkTypeNode  *node;
69   register GtkType new_type;
70
71   /* don't keep *any* GtkTypeNode pointers across invokation of this function!!!
72    */
73
74   if (n_free_type_nodes == 0)
75     {
76       register guint i;
77       register guint size;
78       
79       /* nearest pow
80        */
81       size = n_type_nodes + TYPE_NODES_BLOCK_SIZE;
82       size *= sizeof (GtkTypeNode);
83       i = 1;
84       while (i < size)
85         i <<= 1;
86       size = i;
87       
88       type_nodes = g_realloc (type_nodes, size);
89
90       n_free_type_nodes = size / sizeof (GtkTypeNode) - n_type_nodes;
91       
92       memset (type_nodes + n_type_nodes, 0, n_free_type_nodes * sizeof (GtkTypeNode));
93     }
94
95   new_type = n_type_nodes++;
96   n_free_type_nodes--;
97
98   LOOKUP_TYPE_NODE (node, new_type);
99   if (node)
100     node->type = new_type;
101
102   return node;
103 }
104
105 void
106 gtk_type_init (void)
107 {
108   if (n_type_nodes == 0)
109     {
110       GtkTypeNode *zero;
111
112       g_assert (sizeof (GtkType) >= 4);
113
114       zero = gtk_type_node_next_and_invalidate ();
115       g_assert (zero == NULL);
116
117       type_name_2_type_ht = g_hash_table_new ((GHashFunc) gtk_type_name_hash,
118                                               (GCompareFunc) gtk_type_name_compare);
119
120       gtk_type_init_builtin_types ();
121     }
122 }
123
124 GtkType
125 gtk_type_unique (GtkType      parent_type,
126                  GtkTypeInfo *type_info)
127 {
128   GtkTypeNode *new_node;
129   GtkTypeNode *parent;
130   guint i;
131
132   g_return_val_if_fail (type_info != NULL, 0);
133
134   if (n_type_nodes == 0)
135     gtk_type_init ();
136
137   if (g_hash_table_lookup (type_name_2_type_ht, type_info->type_name))
138     {
139       g_warning ("gtk_type_unique(): type `%s' already exists.", type_info->type_name);
140       return 0;
141     }
142   if (parent_type)
143     {
144       GtkTypeNode *tmp_node;
145
146       LOOKUP_TYPE_NODE (tmp_node, parent_type);
147       if (!tmp_node)
148         {
149           g_warning ("gtk_type_unique(): unknown parent type `%u'.", parent_type);
150           return 0;
151         }
152     }
153
154   /* relookup pointer afterwards.
155    */
156   new_node = gtk_type_node_next_and_invalidate ();
157
158   if (parent_type)
159     {
160       new_node->type = GTK_TYPE_MAKE (parent_type, new_node->type);
161       LOOKUP_TYPE_NODE (parent, parent_type);
162     }
163   else
164     {
165       g_assert (new_node->type <= 0xff);
166       parent = NULL;
167     }
168
169   new_node->type_info = *type_info;
170   new_node->type_info.type_name = g_strdup (type_info->type_name);
171   new_node->n_supers = parent ? parent->n_supers + 1 : 0;
172   new_node->supers = g_new0 (GtkType, new_node->n_supers + 1);
173   new_node->parent_type = parent_type;
174   new_node->klass = NULL;
175   new_node->children_types = NULL;
176
177   if (parent)
178     parent->children_types = g_list_append (parent->children_types, (gpointer) new_node->type);
179
180   parent = new_node;
181   for (i = 0; i < new_node->n_supers + 1; i++)
182     {
183       new_node->supers[i] = parent->type;
184       LOOKUP_TYPE_NODE (parent, parent->parent_type);
185     }
186     
187   g_hash_table_insert (type_name_2_type_ht, new_node->type_info.type_name, (gpointer) new_node->type);
188
189   return new_node->type;
190 }
191
192 gchar*
193 gtk_type_name (GtkType type)
194 {
195   GtkTypeNode *node;
196
197   LOOKUP_TYPE_NODE (node, type);
198
199   if (node)
200     return node->type_info.type_name;
201
202   return NULL;
203 }
204
205 GtkType
206 gtk_type_from_name (const gchar *name)
207 {
208   if (type_name_2_type_ht)
209     {
210       GtkType type;
211       
212       type = (GtkType) g_hash_table_lookup (type_name_2_type_ht, (gpointer) name);
213
214       return type;
215     }
216
217   return 0;
218 }
219
220 GtkType
221 gtk_type_parent (GtkType type)
222 {
223   GtkTypeNode *node;
224
225   LOOKUP_TYPE_NODE (node, type);
226   if (node)
227     return node->parent_type;
228
229   return 0;
230 }
231
232 gpointer
233 gtk_type_parent_class (GtkType type)
234 {
235   GtkTypeNode *node;
236
237   LOOKUP_TYPE_NODE (node, type);
238   g_return_val_if_fail (node != NULL, NULL);
239
240   if (node)
241     {
242       LOOKUP_TYPE_NODE (node, node->parent_type);
243
244       if (node)
245         {
246           if (!node->klass)
247             gtk_type_class_init (node);
248
249           return node->klass;
250         }
251     }
252
253   return NULL;
254 }
255
256 gpointer
257 gtk_type_class (GtkType type)
258 {
259   GtkTypeNode *node;
260
261   LOOKUP_TYPE_NODE (node, type);
262   g_return_val_if_fail (node != NULL, NULL);
263
264   if (!node->klass)
265     gtk_type_class_init (node);
266
267   return node->klass;
268 }
269
270 gpointer
271 gtk_type_new (GtkType type)
272 {
273   GtkTypeNode *node;
274   GtkObject *object;
275   gpointer klass;
276   guint i;
277
278   LOOKUP_TYPE_NODE (node, type);
279   g_return_val_if_fail (node != NULL, NULL);
280
281   klass = gtk_type_class (type);
282   object = g_malloc0 (node->type_info.object_size);
283   object->klass = klass;
284
285   for (i = node->n_supers; i > 0; i--)
286     {
287       GtkTypeNode *pnode;
288
289       LOOKUP_TYPE_NODE (pnode, node->supers[i]);
290       if (pnode->type_info.object_init_func)
291         (* pnode->type_info.object_init_func) (object);
292     }
293   if (node->type_info.object_init_func)
294     (* node->type_info.object_init_func) (object);
295
296   return object;
297 }
298
299 void
300 gtk_type_describe_heritage (GtkType type)
301 {
302   GtkTypeNode *node;
303   gint first;
304
305   LOOKUP_TYPE_NODE (node, type);
306   first = TRUE;
307
308   while (node)
309     {
310       if (first)
311         {
312           first = FALSE;
313           g_print ("is a ");
314         }
315
316       if (node->type_info.type_name)
317         g_print ("%s\n", node->type_info.type_name);
318       else
319         g_print ("<unnamed type>\n");
320
321       LOOKUP_TYPE_NODE (node, node->parent_type);
322     }
323 }
324
325 void
326 gtk_type_describe_tree (GtkType type,
327                         gint  show_size)
328 {
329   GtkTypeNode *node;
330   
331   LOOKUP_TYPE_NODE (node, type);
332   
333   if (node)
334     {
335       static gint indent = 0;
336       GList *list;
337       guint old_indent;
338       guint i;
339       
340       for (i = 0; i < indent; i++)
341         g_print (" ");
342       
343       if (node->type_info.type_name)
344         g_print ("%s", node->type_info.type_name);
345       else
346         g_print ("(no-name)");
347       
348       if (show_size)
349         g_print (" ( %d bytes )\n", node->type_info.object_size);
350       else
351         g_print ("\n");
352       
353       old_indent = indent;
354       indent += 4;
355       
356       for (list = node->children_types; list; list = list->next)
357         gtk_type_describe_tree ((GtkType) list->data, show_size);
358       
359       indent = old_indent;
360     }
361 }
362
363 gint
364 gtk_type_is_a (GtkType type,
365                GtkType is_a_type)
366 {
367   register GtkTypeNode *node;
368
369   /* we already check for type==is_a_type in the
370    * wrapper macro GTK_TYPE_IS_A()
371    */
372
373   LOOKUP_TYPE_NODE (node, type);
374   if (node)
375     {
376       register GtkTypeNode *a_node;
377
378       LOOKUP_TYPE_NODE (a_node, is_a_type);
379       if (a_node)
380         {
381           if (a_node->n_supers <= node->n_supers)
382             return node->supers[node->n_supers - a_node->n_supers] == is_a_type;
383         }
384     }
385
386   return FALSE;
387 }
388
389 void
390 gtk_type_get_arg (GtkObject   *object,
391                   GtkType      type,
392                   GtkArg      *arg,
393                   guint        arg_id)
394 {
395   GtkTypeNode *node;
396
397   g_return_if_fail (object != NULL);
398   g_return_if_fail (arg != NULL);
399
400   LOOKUP_TYPE_NODE (node, type);
401
402   if (node && node->type_info.arg_get_func)
403     (* node->type_info.arg_get_func) (object, arg, arg_id);
404   else
405     arg->type = GTK_TYPE_INVALID;
406 }
407
408 void
409 gtk_type_set_arg (GtkObject *object,
410                   GtkType    type,
411                   GtkArg    *arg,
412                   guint      arg_id)
413 {
414   GtkTypeNode *node;
415
416   g_return_if_fail (object != NULL);
417   g_return_if_fail (arg != NULL);
418
419   LOOKUP_TYPE_NODE (node, type);
420
421   if (node && node->type_info.arg_set_func)
422     (* node->type_info.arg_set_func) (object, arg, arg_id);
423 }
424
425 GtkArg*
426 gtk_arg_copy (GtkArg         *src_arg,
427               GtkArg         *dest_arg)
428 {
429   g_return_val_if_fail (src_arg != NULL, NULL);
430
431   if (!dest_arg)
432     {
433       dest_arg = g_new0 (GtkArg, 1);
434       dest_arg->name = src_arg->name;
435     }
436
437   dest_arg->type = src_arg->type;
438   dest_arg->d = src_arg->d;
439
440   if (src_arg->type == GTK_TYPE_STRING)
441     dest_arg->d.string_data = g_strdup (src_arg->d.string_data);
442
443   return dest_arg;
444 }
445
446 static void
447 gtk_type_class_init (GtkTypeNode *node)
448 {
449   if (!node->klass && node->type_info.class_size)
450     {
451       node->klass = g_new0 (guchar, node->type_info.class_size);
452
453       if (node->parent_type)
454         {
455           GtkTypeNode *parent;
456
457           LOOKUP_TYPE_NODE (parent, node->parent_type);
458           if (!parent->klass)
459             gtk_type_class_init (parent);
460
461           if (parent->klass)
462             memcpy (node->klass, parent->klass, parent->type_info.class_size);
463         }
464
465       if (GTK_TYPE_IS_A (node->type, GTK_TYPE_OBJECT))
466         {
467           GtkObjectClass *object_class;
468
469           /* FIXME: this initialization needs to be done through
470            * a function pointer someday.
471            */
472           g_assert (node->type_info.class_size >= sizeof (GtkObjectClass));
473           
474           object_class = node->klass;
475           object_class->type = node->type;
476           object_class->signals = NULL;
477           object_class->nsignals = 0;
478           object_class->n_args = 0;
479         }
480       
481       if (node->type_info.class_init_func)
482         (* node->type_info.class_init_func) (node->klass);
483     }
484 }
485
486 static guint
487 gtk_type_name_hash (const char *key)
488 {
489   guint result;
490
491   result = 0;
492   while (*key)
493     result += (result << 3) + *key++;
494
495   return result;
496 }
497
498 static gint
499 gtk_type_name_compare (const char *a,
500                        const char *b)
501 {
502   return (strcmp (a, b) == 0);
503 }
504
505 static GtkType
506 gtk_type_register_builtin (char   *name,
507                            GtkType parent)
508 {
509   GtkTypeInfo info;
510
511   info.type_name = name;
512   info.object_size = info.class_size = 0;
513   info.class_init_func = NULL;
514   info.object_init_func = NULL;
515   info.arg_set_func = NULL;
516   info.arg_get_func = NULL;
517
518   return gtk_type_unique (parent, &info);
519 }
520
521 extern void gtk_object_init_type (void);
522
523 GtkType gtk_type_builtins[GTK_TYPE_NUM_BUILTINS];
524
525 static void
526 gtk_type_init_builtin_types (void)
527 {
528   /* GTK_TYPE_INVALID has typeid 0.  The first type id returned by
529      gtk_type_unique is 1, which is GTK_TYPE_NONE.  And so on. */
530
531   static struct {
532     GtkType enum_id;
533     gchar *name;
534   } fundamental_info[] = {
535     { GTK_TYPE_NONE, "void" },
536     { GTK_TYPE_CHAR, "char" },
537     { GTK_TYPE_BOOL, "bool" },
538     { GTK_TYPE_INT, "int" },
539     { GTK_TYPE_UINT, "uint" },
540     { GTK_TYPE_LONG, "long" },
541     { GTK_TYPE_ULONG, "ulong" },
542     { GTK_TYPE_FLOAT, "float" },
543     { GTK_TYPE_DOUBLE, "double" },
544     { GTK_TYPE_STRING, "string" },
545     { GTK_TYPE_ENUM, "enum" },
546     { GTK_TYPE_FLAGS, "flags" },
547     { GTK_TYPE_BOXED, "boxed" },
548     { GTK_TYPE_FOREIGN, "foreign" },
549     { GTK_TYPE_CALLBACK, "callback" },
550     { GTK_TYPE_ARGS, "args" },
551     
552     { GTK_TYPE_POINTER, "pointer" },
553     { GTK_TYPE_SIGNAL, "signal" },
554     { GTK_TYPE_C_CALLBACK, "c_callback" }
555   };
556   
557   static struct {
558     char *name;
559     GtkType parent;
560   } builtin_info[] = {
561 #include "gtktypebuiltins.c"
562     { NULL }
563   };
564   
565   int i;
566   
567   for (i = 0; i < sizeof (fundamental_info) / sizeof (fundamental_info[0]); i++)
568     {
569       GtkType id;
570       id = gtk_type_register_builtin (fundamental_info[i].name,
571                                       GTK_TYPE_INVALID);
572       g_assert (id == fundamental_info[i].enum_id);
573     }
574   
575   gtk_object_init_type ();
576   
577   for (i = 0; builtin_info[i].name; i++)
578     {
579       gtk_type_builtins[i] =
580         gtk_type_register_builtin (builtin_info[i].name,
581                                    builtin_info[i].parent);
582     }
583 }