]> Pileus Git - ~andy/gtk/blob - gtk/gtktypeutils.c
restricted the --g*fatal-* arguments to --g-fatal-warnings again. this
[~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 #include "gtkcontainer.h"
23
24
25 #define TYPE_NODES_BLOCK_SIZE   (200)
26
27 typedef struct _GtkTypeNode GtkTypeNode;
28
29 struct _GtkTypeNode
30 {
31   GtkType type;
32   GtkTypeInfo type_info;
33   guint n_supers : 24;
34   guint chunk_alloc_locked : 1;
35   GtkType *supers;
36   GtkType parent_type;
37   gpointer klass;
38   GList *children_types;
39   GMemChunk *mem_chunk;
40 };
41
42
43 #define LOOKUP_TYPE_NODE(node_var, type)        { \
44   if (type > 0) \
45   { \
46     register GtkType sqn = GTK_TYPE_SEQNO (type); \
47     if (sqn < n_type_nodes) \
48       node_var = type_nodes + sqn; \
49     else \
50       node_var = NULL; \
51   } \
52   else \
53     node_var = NULL; \
54 }
55
56 static void  gtk_type_class_init                (GtkType      node_type);
57 static guint gtk_type_name_hash                 (const char  *key);
58 static gint  gtk_type_name_compare              (const char  *a,
59                                                  const char  *b);
60 static void  gtk_type_init_builtin_types        (void);
61
62 static GtkTypeNode *type_nodes = NULL;
63 static guint        n_type_nodes = 0;
64 static GHashTable  *type_name_2_type_ht = NULL;
65
66
67 static GtkTypeNode*
68 gtk_type_node_next_and_invalidate (void)
69 {
70   static guint  n_free_type_nodes = 0;
71   register GtkTypeNode  *node;
72   register GtkType new_type;
73   
74   /* don't keep *any* GtkTypeNode pointers across invokation of this function!!!
75    */
76   
77   if (n_free_type_nodes == 0)
78     {
79       register guint i;
80       register guint size;
81       
82       /* nearest pow
83        */
84       size = n_type_nodes + TYPE_NODES_BLOCK_SIZE;
85       size *= sizeof (GtkTypeNode);
86       i = 1;
87       while (i < size)
88         i <<= 1;
89       size = i;
90       
91       type_nodes = g_realloc (type_nodes, size);
92       
93       n_free_type_nodes = size / sizeof (GtkTypeNode) - n_type_nodes;
94       
95       memset (type_nodes + n_type_nodes, 0, n_free_type_nodes * sizeof (GtkTypeNode));
96     }
97   
98   new_type = n_type_nodes++;
99   n_free_type_nodes--;
100   
101   LOOKUP_TYPE_NODE (node, new_type);
102   if (node)
103     node->type = new_type;
104   
105   return node;
106 }
107
108 void
109 gtk_type_init (void)
110 {
111   if (n_type_nodes == 0)
112     {
113       GtkTypeNode *zero;
114       
115       g_assert (sizeof (GtkType) >= 4);
116       
117       zero = gtk_type_node_next_and_invalidate ();
118       g_assert (zero == NULL);
119       
120       type_name_2_type_ht = g_hash_table_new ((GHashFunc) gtk_type_name_hash,
121                                               (GCompareFunc) gtk_type_name_compare);
122       
123       gtk_type_init_builtin_types ();
124     }
125 }
126
127 void
128 gtk_type_set_chunk_alloc (GtkType      type,
129                           guint        n_chunks)
130 {
131   GtkTypeNode *node;
132   
133   LOOKUP_TYPE_NODE (node, type);
134   g_return_if_fail (node != NULL);
135   g_return_if_fail (node->chunk_alloc_locked == FALSE);
136   
137   if (node->mem_chunk)
138     {
139       g_mem_chunk_destroy (node->mem_chunk);
140       node->mem_chunk = NULL;
141     }
142   
143   if (n_chunks)
144     node->mem_chunk = g_mem_chunk_new (node->type_info.type_name,
145                                        node->type_info.object_size,
146                                        node->type_info.object_size * n_chunks,
147                                        G_ALLOC_AND_FREE);
148 }
149
150 static GtkType
151 gtk_type_create (GtkType      parent_type,
152                  gchar        *type_name,
153                  GtkTypeInfo *type_info)
154 {
155   GtkTypeNode *new_node;
156   GtkTypeNode *parent;
157   guint i;
158   
159   if (g_hash_table_lookup (type_name_2_type_ht, type_name))
160     {
161       g_warning ("gtk_type_create(): type `%s' already exists.", type_name);
162       return 0;
163     }
164   
165   if (parent_type)
166     {
167       GtkTypeNode *tmp_node;
168       
169       LOOKUP_TYPE_NODE (tmp_node, parent_type);
170       if (!tmp_node)
171         {
172           g_warning ("gtk_type_create(): unknown parent type `%u'.", parent_type);
173           return 0;
174         }
175     }
176   
177   /* relookup pointers afterwards.
178    */
179   new_node = gtk_type_node_next_and_invalidate ();
180   
181   if (parent_type)
182     {
183       new_node->type = GTK_TYPE_MAKE (parent_type, new_node->type);
184       LOOKUP_TYPE_NODE (parent, parent_type);
185     }
186   else
187     {
188       g_assert (new_node->type <= 0xff);
189       parent = NULL;
190     }
191   
192   new_node->type_info = *type_info;
193   new_node->type_info.type_name = type_name;
194   /* new_node->type_info.reserved_1 = NULL; */
195   new_node->type_info.reserved_2 = NULL;
196   new_node->n_supers = parent ? parent->n_supers + 1 : 0;
197   new_node->chunk_alloc_locked = FALSE;
198   new_node->supers = g_new0 (GtkType, new_node->n_supers + 1);
199   new_node->parent_type = parent_type;
200   new_node->klass = NULL;
201   new_node->children_types = NULL;
202   new_node->mem_chunk = NULL;
203   
204   if (parent)
205     parent->children_types = g_list_append (parent->children_types, GUINT_TO_POINTER (new_node->type));
206   
207   parent = new_node;
208   for (i = 0; i < new_node->n_supers + 1; i++)
209     {
210       new_node->supers[i] = parent->type;
211       LOOKUP_TYPE_NODE (parent, parent->parent_type);
212     }
213   
214   g_hash_table_insert (type_name_2_type_ht, new_node->type_info.type_name, GUINT_TO_POINTER (new_node->type));
215   
216   return new_node->type;
217 }
218
219 GtkType
220 gtk_type_unique (GtkType      parent_type,
221                  GtkTypeInfo *type_info)
222 {
223   GtkType new_type;
224   gchar *type_name;
225   
226   g_return_val_if_fail (type_info != NULL, 0);
227   g_return_val_if_fail (type_info->type_name != NULL, 0);
228   
229   if (n_type_nodes == 0)
230     gtk_type_init ();
231   
232   type_name = g_strdup (type_info->type_name);
233   
234   /* relookup pointers afterwards.
235    */
236   new_type = gtk_type_create (parent_type, type_name, type_info);
237   
238   if (!new_type)
239     g_free (type_name);
240   
241   return new_type;
242 }
243
244 gchar*
245 gtk_type_name (GtkType type)
246 {
247   GtkTypeNode *node;
248   
249   LOOKUP_TYPE_NODE (node, type);
250   
251   if (node)
252     return node->type_info.type_name;
253   
254   return NULL;
255 }
256
257 GtkType
258 gtk_type_from_name (const gchar *name)
259 {
260   if (type_name_2_type_ht)
261     {
262       GtkType type;
263       
264       type = GPOINTER_TO_UINT (g_hash_table_lookup (type_name_2_type_ht, (gpointer) name));
265       
266       return type;
267     }
268   
269   return 0;
270 }
271
272 GtkType
273 gtk_type_parent (GtkType type)
274 {
275   GtkTypeNode *node;
276   
277   LOOKUP_TYPE_NODE (node, type);
278   if (node)
279     return node->parent_type;
280   
281   return 0;
282 }
283
284 gpointer
285 gtk_type_parent_class (GtkType type)
286 {
287   GtkTypeNode *node;
288   
289   LOOKUP_TYPE_NODE (node, type);
290   g_return_val_if_fail (node != NULL, NULL);
291   
292   if (node)
293     {
294       LOOKUP_TYPE_NODE (node, node->parent_type);
295       
296       if (node)
297         {
298           if (!node->klass)
299             {
300               type = node->type;
301               gtk_type_class_init (type);
302               LOOKUP_TYPE_NODE (node, type);
303             }
304           
305           return node->klass;
306         }
307     }
308   
309   return NULL;
310 }
311
312 gpointer
313 gtk_type_class (GtkType type)
314 {
315   GtkTypeNode *node;
316   
317   LOOKUP_TYPE_NODE (node, type);
318   g_return_val_if_fail (node != NULL, NULL);
319   
320   if (!node->klass)
321     {
322       type = node->type;
323       gtk_type_class_init (type);
324       LOOKUP_TYPE_NODE (node, type);
325     }
326   
327   return node->klass;
328 }
329
330 gpointer
331 gtk_type_new (GtkType type)
332 {
333   GtkTypeNode *node;
334   GtkObject *object;
335   gpointer klass;
336   guint i;
337   
338   LOOKUP_TYPE_NODE (node, type);
339   g_return_val_if_fail (node != NULL, NULL);
340   
341   klass = gtk_type_class (type);
342   node->chunk_alloc_locked = TRUE;
343   if (node->mem_chunk)
344     {
345       object = g_mem_chunk_alloc (node->mem_chunk);
346       memset (object, 0, node->type_info.object_size);
347     }
348   else
349     object = g_malloc0 (node->type_info.object_size);
350   
351   /* we need to call the base classes' object_init_func for derived
352    * objects with the object's ->klass field still pointing to the
353    * corresponding base class, otherwise overridden class functions
354    * could get called with partly-initialized objects.
355    */
356   for (i = node->n_supers; i > 0; i--)
357     {
358       GtkTypeNode *pnode;
359       
360       LOOKUP_TYPE_NODE (pnode, node->supers[i]);
361       if (pnode->type_info.object_init_func)
362         {
363           object->klass = pnode->klass;
364           pnode->type_info.object_init_func (object);
365         }
366     }
367   object->klass = klass;
368   if (node->type_info.object_init_func)
369     node->type_info.object_init_func (object);
370   
371   return object;
372 }
373
374 void
375 gtk_type_free (GtkType      type,
376                gpointer     mem)
377 {
378   GtkTypeNode *node;
379   
380   g_return_if_fail (mem != NULL);
381   LOOKUP_TYPE_NODE (node, type);
382   g_return_if_fail (node != NULL);
383   
384   if (node->mem_chunk)
385     g_mem_chunk_free (node->mem_chunk, mem);
386   else
387     g_free (mem);
388 }
389
390 GList*
391 gtk_type_children_types (GtkType type)
392 {
393   GtkTypeNode *node;
394   
395   LOOKUP_TYPE_NODE (node, type);
396   if (node)
397     return node->children_types;
398   
399   return NULL;
400 }
401
402 void
403 gtk_type_describe_heritage (GtkType type)
404 {
405   GtkTypeNode *node;
406   gboolean first;
407   
408   LOOKUP_TYPE_NODE (node, type);
409   first = TRUE;
410   
411   while (node)
412     {
413       if (first)
414         {
415           first = FALSE;
416           g_message ("is a ");
417         }
418       
419       if (node->type_info.type_name)
420         g_message ("%s\n", node->type_info.type_name);
421       else
422         g_message ("<unnamed type>\n");
423       
424       LOOKUP_TYPE_NODE (node, node->parent_type);
425     }
426 }
427
428 void
429 gtk_type_describe_tree (GtkType  type,
430                         gboolean show_size)
431 {
432   GtkTypeNode *node;
433   
434   LOOKUP_TYPE_NODE (node, type);
435   
436   if (node)
437     {
438       static gint indent = 0;
439       GList *list;
440       guint old_indent;
441       guint i;
442       
443       for (i = 0; i < indent; i++)
444         g_message (" ");
445       
446       if (node->type_info.type_name)
447         g_message ("%s", node->type_info.type_name);
448       else
449         g_message ("(no-name)");
450       
451       if (show_size)
452         g_message (" ( %d bytes )\n", node->type_info.object_size);
453       else
454         g_message ("\n");
455       
456       old_indent = indent;
457       indent += 4;
458       
459       for (list = node->children_types; list; list = list->next)
460         gtk_type_describe_tree (GPOINTER_TO_UINT (list->data), show_size);
461       
462       indent = old_indent;
463     }
464 }
465
466 gint
467 gtk_type_is_a (GtkType type,
468                GtkType is_a_type)
469 {
470   if (type == is_a_type)
471     return TRUE;
472   else
473     {
474       register GtkTypeNode *node;
475       
476       LOOKUP_TYPE_NODE (node, type);
477       if (node)
478         {
479           register GtkTypeNode *a_node;
480           
481           LOOKUP_TYPE_NODE (a_node, is_a_type);
482           if (a_node)
483             {
484               if (a_node->n_supers <= node->n_supers)
485                 return node->supers[node->n_supers - a_node->n_supers] == is_a_type;
486             }
487         }
488     }
489   
490   return FALSE;
491 }
492
493 static void
494 gtk_type_class_init (GtkType type)
495 {
496   GtkTypeNode *node;
497
498   /* we need to relookup nodes everytime we called an external function */
499   LOOKUP_TYPE_NODE (node, type);
500   
501   if (!node->klass && node->type_info.class_size)
502     {
503       GtkObjectClass *object_class;
504       GtkTypeNode *base_node;
505       GSList *slist;
506       
507       g_assert (node->type_info.class_size >= sizeof (GtkObjectClass));
508       
509       node->klass = g_malloc0 (node->type_info.class_size);
510       
511       if (node->parent_type)
512         {
513           GtkTypeNode *parent;
514           
515           LOOKUP_TYPE_NODE (parent, node->parent_type);
516           if (!parent->klass)
517             {
518               gtk_type_class_init (parent->type);
519               LOOKUP_TYPE_NODE (node, type);
520               LOOKUP_TYPE_NODE (parent, node->parent_type);
521             }
522           
523           if (parent->klass)
524             memcpy (node->klass, parent->klass, parent->type_info.class_size);
525         }
526       
527       object_class = node->klass;
528       object_class->type = node->type;
529       
530       /* stack all base class initialization functions, so we
531        * call them in ascending order.
532        */
533       base_node = node;
534       slist = NULL;
535       while (base_node)
536         {
537           if (base_node->type_info.base_class_init_func)
538             slist = g_slist_prepend (slist, base_node->type_info.base_class_init_func);
539           LOOKUP_TYPE_NODE (base_node, base_node->parent_type);
540         }
541       if (slist)
542         {
543           GSList *walk;
544           
545           for (walk = slist; walk; walk = walk->next)
546             {
547               register GtkClassInitFunc base_class_init;
548               
549               base_class_init = walk->data;
550               base_class_init (node->klass);
551               LOOKUP_TYPE_NODE (node, type);
552             }
553           g_slist_free (slist);
554         }
555       
556       if (node->type_info.class_init_func)
557         node->type_info.class_init_func (node->klass);
558     }
559 }
560
561 GtkEnumValue*
562 gtk_type_enum_get_values (GtkType      enum_type)
563 {
564   if (GTK_FUNDAMENTAL_TYPE (enum_type) == GTK_TYPE_ENUM ||
565       GTK_FUNDAMENTAL_TYPE (enum_type) == GTK_TYPE_FLAGS)
566     {
567       GtkTypeNode *node;
568       
569       LOOKUP_TYPE_NODE (node, enum_type);
570       if (node)
571         return (GtkEnumValue*) node->type_info.reserved_1;
572     }
573   
574   g_warning ("gtk_type_enum_get_values(): type `%s' is not derived from `GtkEnum' or `GtkFlags'",
575              gtk_type_name (enum_type));
576   
577   return NULL;
578 }
579
580 GtkFlagValue*
581 gtk_type_flags_get_values (GtkType        flags_type)
582 {
583   return gtk_type_enum_get_values (flags_type);
584 }
585
586 GtkEnumValue*
587 gtk_type_enum_find_value (GtkType        enum_type,
588                           const gchar    *value_name)
589 {
590   g_return_val_if_fail (value_name != NULL, NULL);
591   
592   if (GTK_FUNDAMENTAL_TYPE (enum_type) == GTK_TYPE_ENUM ||
593       GTK_FUNDAMENTAL_TYPE (enum_type) == GTK_TYPE_FLAGS)
594     {
595       GtkEnumValue *vals;
596
597       vals = gtk_type_enum_get_values (enum_type);
598       while (vals)
599         {
600           if (strcmp (vals->value_name, value_name) == 0 ||
601               strcmp (vals->value_nick, value_name) == 0)
602             return vals;
603           vals++;
604         }
605     }
606   else
607     g_warning ("gtk_type_enum_find_value(): type `%s' is not derived from `GtkEnum' or `GtkFlags'",
608                gtk_type_name (enum_type));
609
610   return NULL;
611 }
612
613 GtkFlagValue*
614 gtk_type_flags_find_value (GtkType         flag_type,
615                            const gchar    *value_name)
616 {
617   g_return_val_if_fail (value_name != NULL, NULL);
618
619   return gtk_type_enum_find_value (flag_type, value_name);
620 }
621
622 static inline GtkType
623 gtk_type_register_intern (gchar        *name,
624                           GtkType       parent,
625                           GtkEnumValue *values)
626 {
627   GtkType type_id;
628   GtkTypeInfo info;
629   
630   info.type_name = name;
631   info.object_size = 0;
632   info.class_size = 0;
633   info.class_init_func = NULL;
634   info.object_init_func = NULL;
635   info.reserved_1 = values;
636   info.reserved_2 = NULL;
637   
638   /* relookup pointers afterwards.
639    */
640   type_id = gtk_type_create (parent, name, &info);
641   
642   if (type_id && values)
643     {
644       guint i;
645       
646       /* check for proper type consistency and NULL termination
647        * of value array
648        */
649       g_assert (GTK_FUNDAMENTAL_TYPE (type_id) == GTK_TYPE_ENUM ||
650                 GTK_FUNDAMENTAL_TYPE (type_id) == GTK_TYPE_FLAGS);
651       
652       i = 0;
653       while (values[i].value_name)
654         i++;
655       
656       g_assert (values[i].value_name == NULL && values[i].value_nick == NULL);
657     }
658   
659   return type_id;
660 }
661
662 GtkType
663 gtk_type_register_enum (const gchar    *type_name,
664                         GtkEnumValue   *values)
665 {
666   GtkType type_id;
667   gchar *name;
668   
669   g_return_val_if_fail (type_name != NULL, 0);
670   
671   name = g_strdup (type_name);
672   
673   /* relookup pointers afterwards.
674    */
675   type_id = gtk_type_register_intern (name, GTK_TYPE_ENUM, values);
676   
677   if (!type_id)
678     g_free (name);
679   
680   return type_id;
681 }
682
683 GtkType
684 gtk_type_register_flags (const gchar    *type_name,
685                          GtkFlagValue   *values)
686 {
687   GtkType type_id;
688   gchar *name;
689   
690   g_return_val_if_fail (type_name != NULL, 0);
691   
692   name = g_strdup (type_name);
693   
694   /* relookup pointers afterwards.
695    */
696   type_id = gtk_type_register_intern (name, GTK_TYPE_FLAGS, values);
697   
698   if (!type_id)
699     g_free (name);
700   
701   return type_id;
702 }
703
704 static guint
705 gtk_type_name_hash (const char *key)
706 {
707   guint result;
708   
709   result = 0;
710   while (*key)
711     result += (result << 3) + *key++;
712   
713   return result;
714 }
715
716 static gint
717 gtk_type_name_compare (const char *a,
718                        const char *b)
719 {
720   return (strcmp (a, b) == 0);
721 }
722
723 extern void gtk_object_init_type (void);
724
725 #include "makeenums.h"                  /* include for various places
726                                          * with enum definitions
727                                          */
728 #include "gtktypebuiltins_vars.c"       /* type variable declarations
729                                          */
730 #include "gtktypebuiltins_evals.c"      /* enum value definition arrays
731                                          */
732
733 static void
734 gtk_type_init_builtin_types (void)
735 {
736   /* GTK_TYPE_INVALID has typeid 0.  The first type id returned by
737    * gtk_type_unique is 1, which is GTK_TYPE_NONE.  And so on.
738    */
739   
740   struct {
741     GtkType type_id;
742     gchar *name;
743   } fundamental_info[] = {
744     { GTK_TYPE_NONE,            "void" },
745     { GTK_TYPE_CHAR,            "gchar" },
746     { GTK_TYPE_UCHAR,           "guchar" },
747     { GTK_TYPE_BOOL,            "gboolean" },
748     { GTK_TYPE_INT,             "gint" },
749     { GTK_TYPE_UINT,            "guint" },
750     { GTK_TYPE_LONG,            "glong" },
751     { GTK_TYPE_ULONG,           "gulong" },
752     { GTK_TYPE_FLOAT,           "gfloat" },
753     { GTK_TYPE_DOUBLE,          "gdouble" },
754     { GTK_TYPE_STRING,          "GtkString" },
755     { GTK_TYPE_ENUM,            "GtkEnum" },
756     { GTK_TYPE_FLAGS,           "GtkFlags" },
757     { GTK_TYPE_BOXED,           "GtkBoxed" },
758     { GTK_TYPE_POINTER,         "gpointer" },
759     
760     { GTK_TYPE_SIGNAL,          "GtkSignal" },
761     { GTK_TYPE_ARGS,            "GtkArgs" },
762     { GTK_TYPE_CALLBACK,        "GtkCallback" },
763     { GTK_TYPE_C_CALLBACK,      "GtkCCallback" },
764     { GTK_TYPE_FOREIGN,         "GtkForeign" },
765   };
766   struct {
767     gchar *type_name;
768     GtkType *type_id;
769     GtkType parent;
770     GtkEnumValue *values;
771   } builtin_info[GTK_TYPE_NUM_BUILTINS + 1] = {
772 #include "gtktypebuiltins_ids.c"        /* type entries */
773     { NULL }
774   };
775   guint i;
776   
777   for (i = 0; i < sizeof (fundamental_info) / sizeof (fundamental_info[0]); i++)
778     {
779       GtkType type_id;
780       
781       /* relookup pointers afterwards.
782        */
783       type_id = gtk_type_register_intern (fundamental_info[i].name, GTK_TYPE_INVALID, NULL);
784       
785       g_assert (type_id == fundamental_info[i].type_id);
786     }
787   
788   gtk_object_init_type ();
789   
790   for (i = 0; i < GTK_TYPE_NUM_BUILTINS; i++)
791     {
792       GtkType type_id;
793       
794       g_assert (builtin_info[i].type_name != NULL);
795       
796       /* relookup pointers afterwards.
797        */
798       type_id = gtk_type_register_intern (builtin_info[i].type_name,
799                                           builtin_info[i].parent,
800                                           builtin_info[i].values);
801       
802       g_assert (type_id != GTK_TYPE_INVALID);
803       
804       (*builtin_info[i].type_id) = type_id;
805     }
806 }
807
808 GtkType
809 gtk_identifier_get_type (void)
810 {
811   static GtkType identifier_type = 0;
812   
813   if (!identifier_type)
814     identifier_type = gtk_type_register_intern ("GtkIdentifier", GTK_TYPE_STRING, NULL);
815   
816   return identifier_type;
817 }