]> Pileus Git - ~andy/gtk/blob - gtk/gtkobject.c
made the object/class cast checks much more descriptive, based on
[~andy/gtk] / gtk / gtkobject.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 Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <stdarg.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include "gtkobject.h"
23 #include "gtksignal.h"
24
25
26 #define OBJECT_DATA_ID_CHUNK  1024
27
28
29 enum {
30   DESTROY,
31   LAST_SIGNAL
32 };
33 enum {
34   ARG_0,
35   ARG_USER_DATA,
36   ARG_SIGNAL,
37   ARG_OBJECT_SIGNAL
38 };
39
40
41 typedef struct _GtkObjectData  GtkObjectData;
42 typedef struct _GtkArgInfo     GtkArgInfo;
43
44 struct _GtkObjectData
45 {
46   guint id;
47   gpointer data;
48   GtkDestroyNotify destroy;
49   GtkObjectData *next;
50 };
51
52 struct _GtkArgInfo
53 {
54   char *name;
55   GtkType type;
56   GtkType class_type;
57   guint arg_flags;
58   guint arg_id;
59   guint seq_id;
60 };
61
62
63 static void           gtk_object_class_init    (GtkObjectClass *klass);
64 static void           gtk_object_init          (GtkObject      *object);
65 static void           gtk_object_set_arg       (GtkObject      *object,
66                                                 GtkArg         *arg,
67                                                 guint           arg_id);
68 static void           gtk_object_get_arg       (GtkObject      *object,
69                                                 GtkArg         *arg,
70                                                 guint           arg_id);
71 static void           gtk_object_real_destroy  (GtkObject      *object);
72 static void           gtk_object_finalize      (GtkObject      *object);
73 static void           gtk_object_notify_weaks  (gpointer        data);
74 static void           gtk_object_data_init     (void);
75 static GtkObjectData* gtk_object_data_new      (void);
76 static void           gtk_object_data_destroy  (GtkObjectData  *odata);
77 static guint*         gtk_object_data_id_alloc (void);
78
79 GtkArg*               gtk_object_collect_args  (guint   *nargs,
80                                                 va_list  args1,
81                                                 va_list  args2);
82
83 static gint object_signals[LAST_SIGNAL] = { 0 };
84
85 static gint object_data_init = TRUE;
86 static GHashTable *object_data_ht = NULL;
87 static GMemChunk *object_data_mem_chunk = NULL;
88 static GSList *object_data_id_list = NULL;
89 static gint object_data_id_index = 0;
90
91 static GHashTable *arg_info_ht = NULL;
92
93 static const char *user_data_key = "user_data";
94
95
96 static gint obj_count = 0;
97 static GSList *living_objs = NULL;
98
99
100 static void
101 gtk_object_debug (void)
102 {
103   if (1)
104     {
105       GSList *node;
106       
107       printf ("living objects (%d):\n", g_slist_length (living_objs));
108       for (node = living_objs; node; node = node->next)
109         {
110           GtkObject *obj;
111           
112           obj = (GtkObject*) node->data;
113           printf ("%p: %s ref_count=%d%s%s\n",
114                   obj, gtk_type_name (GTK_OBJECT_TYPE (obj)),
115                   obj->ref_count,
116                   GTK_OBJECT_FLOATING (obj) ? " (floating)" : "",
117                   GTK_OBJECT_DESTROYED (obj) ? " (destroyed)" : "");
118         }
119     }
120   printf ("living objects count = %d\n", obj_count);
121 }
122
123 /*****************************************
124  * gtk_object_init_type:
125  *
126  *   arguments:
127  *
128  *   results:
129  *****************************************/
130
131 void
132 gtk_object_init_type ()
133 {
134   GtkType object_type = 0;
135   GtkTypeInfo object_info =
136   {
137     "GtkObject",
138     sizeof (GtkObject),
139     sizeof (GtkObjectClass),
140     (GtkClassInitFunc) gtk_object_class_init,
141     (GtkObjectInitFunc) gtk_object_init,
142     gtk_object_set_arg,
143     gtk_object_get_arg,
144   };
145
146   object_type = gtk_type_unique (0, &object_info);
147   g_assert (object_type == GTK_TYPE_OBJECT);
148
149 #ifdef G_ENABLE_DEBUG
150   if (gtk_debug_flags & GTK_DEBUG_OBJECTS)
151     ATEXIT (gtk_object_debug);
152 #endif  /* G_ENABLE_DEBUG */
153 }
154
155 GtkType
156 gtk_object_get_type ()
157 {
158   return GTK_TYPE_OBJECT;
159 }
160
161 /*****************************************
162  * gtk_object_class_init:
163  *
164  *   arguments:
165  *
166  *   results:
167  *****************************************/
168
169 static void
170 gtk_object_class_init (GtkObjectClass *class)
171 {
172   class->signals = NULL;
173   class->nsignals = 0;
174   class->n_args = 0;
175
176   gtk_object_add_arg_type ("GtkObject::user_data",
177                            GTK_TYPE_POINTER,
178                            GTK_ARG_READWRITE,
179                            ARG_USER_DATA);
180   gtk_object_add_arg_type ("GtkObject::signal",
181                            GTK_TYPE_SIGNAL,
182                            GTK_ARG_WRITABLE,
183                            ARG_SIGNAL);
184   gtk_object_add_arg_type ("GtkObject::object_signal",
185                            GTK_TYPE_SIGNAL,
186                            GTK_ARG_WRITABLE,
187                            ARG_OBJECT_SIGNAL);
188
189   object_signals[DESTROY] =
190     gtk_signal_new ("destroy",
191                     GTK_RUN_LAST,
192                     class->type,
193                     GTK_SIGNAL_OFFSET (GtkObjectClass, destroy),
194                     gtk_signal_default_marshaller,
195                     GTK_TYPE_NONE, 0);
196
197   gtk_object_class_add_signals (class, object_signals, LAST_SIGNAL);
198
199   class->destroy = gtk_object_real_destroy;
200   class->finalize = gtk_object_finalize;
201 }
202
203 /*****************************************
204  * gtk_object_real_destroy:
205  *
206  *   arguments:
207  *
208  *   results:
209  *****************************************/
210
211 static void
212 gtk_object_real_destroy (GtkObject *object)
213 {
214   g_return_if_fail (object != NULL);
215   g_return_if_fail (GTK_IS_OBJECT (object));
216
217   gtk_signal_handlers_destroy (object);
218 }
219
220 /*****************************************
221  * gtk_object_init:
222  *
223  *   arguments:
224  *
225  *   results:
226  *****************************************/
227
228 static void
229 gtk_object_init (GtkObject *object)
230 {
231   GTK_OBJECT_FLAGS (object) = GTK_FLOATING;
232
233   object->ref_count = 1;
234   object->object_data = NULL;
235
236 #ifdef G_ENABLE_DEBUG
237   if (gtk_debug_flags & GTK_DEBUG_OBJECTS)
238     {
239       obj_count++;
240       living_objs = g_slist_prepend (living_objs, object);
241     }
242 #endif /* G_ENABLE_DEBUG */
243 }
244
245 /*****************************************
246  * gtk_object_set_arg:
247  *
248  *   arguments:
249  *
250  *   results:
251  *****************************************/
252
253 static void
254 gtk_object_set_arg (GtkObject *object,
255                     GtkArg    *arg,
256                     guint      arg_id)
257 {
258   switch (arg_id)
259     {
260     case ARG_USER_DATA:
261       gtk_object_set_user_data (object, GTK_VALUE_POINTER (*arg));
262       break;
263     case ARG_SIGNAL:
264       if ((arg->name[9 + 2 + 6] != ':') || (arg->name[9 + 2 + 7] != ':'))
265         {
266           g_warning ("invalid signal argument: \"%s\"\n", arg->name);
267           arg->type = GTK_TYPE_INVALID;
268           return;
269         }
270       gtk_signal_connect (object, arg->name + 9 + 2 + 6 + 2,
271                           (GtkSignalFunc) GTK_VALUE_SIGNAL (*arg).f,
272                           GTK_VALUE_SIGNAL (*arg).d);
273       break;
274     case ARG_OBJECT_SIGNAL:
275       if ((arg->name[9 + 2 + 13] != ':') || (arg->name[9 + 2 + 14] != ':'))
276         {
277           g_warning ("invalid signal argument: \"%s\"\n", arg->name);
278           arg->type = GTK_TYPE_INVALID;
279           return;
280         }
281       gtk_signal_connect_object (object, arg->name + 9 + 2 + 13 + 2,
282                                  (GtkSignalFunc) GTK_VALUE_SIGNAL (*arg).f,
283                                  (GtkObject*) GTK_VALUE_SIGNAL (*arg).d);
284       break;
285     default:
286       arg->type = GTK_TYPE_INVALID;
287       break;
288     }
289 }
290
291 /*****************************************
292  * gtk_object_get_arg:
293  *
294  *   arguments:
295  *
296  *   results:
297  *****************************************/
298
299 static void
300 gtk_object_get_arg (GtkObject *object,
301                     GtkArg    *arg,
302                     guint      arg_id)
303 {
304   switch (arg_id)
305     {
306     case ARG_USER_DATA:
307       GTK_VALUE_POINTER (*arg) = gtk_object_get_user_data (object);
308       break;
309     case ARG_SIGNAL:
310     case ARG_OBJECT_SIGNAL:
311     default:
312       arg->type = GTK_TYPE_INVALID;
313       break;
314     }
315 }
316
317 /*****************************************
318  * gtk_object_class_add_signals:
319  *
320  *   arguments:
321  *
322  *   results:
323  *****************************************/
324
325 void
326 gtk_object_class_add_signals (GtkObjectClass *class,
327                               gint           *signals,
328                               gint            nsignals)
329 {
330   gint *new_signals;
331   gint i;
332
333   g_return_if_fail (class != NULL);
334
335   new_signals = g_new (gint, class->nsignals + nsignals);
336   for (i = 0; i < class->nsignals; i++)
337     new_signals[i] = class->signals[i];
338   for (i = 0; i < nsignals; i++)
339     new_signals[class->nsignals + i] = signals[i];
340
341   g_free (class->signals);
342   class->signals = new_signals;
343   class->nsignals += nsignals;
344 }
345
346 /*****************************************
347  * gtk_object_class_add_user_signal:
348  *
349  *   arguments:
350  *
351  *   results:
352  *****************************************/
353
354 gint
355 gtk_object_class_add_user_signal (GtkObjectClass     *class,
356                                   const gchar        *name,
357                                   GtkSignalMarshaller marshaller,
358                                   GtkType             return_val,
359                                   gint                nparams,
360                                   ...)
361 {
362   GtkType *params;
363   guint i;
364   va_list args;
365   gint signal_id;
366
367   g_return_val_if_fail (class != NULL, 0);
368
369   if (nparams > 0)
370     {
371       params = g_new (GtkType, nparams);
372
373       va_start (args, nparams);
374
375       for (i = 0; i < nparams; i++)
376         params[i] = va_arg (args, GtkType);
377
378       va_end (args);
379     }
380   else
381     params = NULL;
382
383   signal_id = gtk_signal_newv (name,
384                                0,
385                                class->type,
386                                0,
387                                marshaller,
388                                return_val,
389                                nparams,
390                                params);
391
392   g_free (params);
393
394   if (signal_id)
395     gtk_object_class_add_signals (class, &signal_id, 1);
396
397   return signal_id;
398 }
399
400 /*****************************************
401  * gtk_object_finalize:
402  *
403  *   arguments:
404  *
405  *   results:
406  *****************************************/
407
408 static void
409 gtk_object_finalize (GtkObject *object)
410 {
411   GtkObjectData *odata, *next;
412   
413   odata = object->object_data;
414   while (odata)
415     {
416       next = odata->next;
417       gtk_object_data_destroy (odata);
418       odata = next;
419     }
420   
421   g_free (object);
422 }
423
424 /*****************************************
425  * gtk_object_sink:
426  *
427  *   arguments:
428  *
429  *   results:
430  *****************************************/
431
432 void
433 gtk_object_sink (GtkObject *object)
434 {
435   g_return_if_fail (object != NULL);
436   g_return_if_fail (GTK_IS_OBJECT (object));
437
438   if (GTK_OBJECT_FLOATING (object))
439     {
440       GTK_OBJECT_UNSET_FLAGS (object, GTK_FLOATING);
441       gtk_object_unref (object);
442     }
443 }
444
445 /*****************************************
446  * gtk_object_destroy:
447  *
448  *   arguments:
449  *
450  *   results:
451  *****************************************/
452
453 void
454 gtk_object_destroy (GtkObject *object)
455 {
456   g_return_if_fail (object != NULL);
457   g_return_if_fail (GTK_IS_OBJECT (object));
458
459   if (!GTK_OBJECT_DESTROYED (object))
460     {
461       GTK_OBJECT_SET_FLAGS (object, GTK_DESTROYED);
462       gtk_signal_emit (object, object_signals[DESTROY]);
463     }
464 }
465
466 /*****************************************
467  * Weak references.
468  *
469  * Weak refs are very similar to the old "destroy" signal.  They allow
470  * one to register a callback that is called when the weakly
471  * referenced object is finalized.
472  *  
473  * They are not implemented as a signal because they really are
474  * special and need to be used with great care.  Unlike signals, who
475  * should be able to execute any code whatsoever.
476  * 
477  * A weakref callback is not allowed to retain a reference to the
478  * object.  In fact, the object is no longer there at all when it is
479  * called.
480  * 
481  * A weakref callback is called atmost once.
482  *
483  *****************************************/
484
485 typedef struct _GtkWeakRef      GtkWeakRef;
486
487 struct _GtkWeakRef
488 {
489   GtkWeakRef       *next;
490   GtkDestroyNotify  notify;
491   gpointer          data;
492 };
493
494 static const gchar *weakrefs_key = "gtk-weakrefs";
495
496 void
497 gtk_object_weakref (GtkObject        *object,
498                     GtkDestroyNotify  notify,
499                     gpointer          data)
500 {
501   GtkWeakRef *weak;
502
503   g_return_if_fail (object != NULL);
504   g_return_if_fail (notify != NULL);
505   g_return_if_fail (GTK_IS_OBJECT (object));
506
507   weak = g_new (GtkWeakRef, 1);
508   weak->next = gtk_object_get_data (object, weakrefs_key);
509   weak->notify = notify;
510   weak->data = data;
511   gtk_object_set_data_full (object, weakrefs_key, weak, 
512                             gtk_object_notify_weaks);
513 }
514
515 void
516 gtk_object_weakunref (GtkObject        *object,
517                       GtkDestroyNotify  notify,
518                       gpointer          data)
519 {
520   GtkWeakRef *weaks, *w, **wp;
521
522   g_return_if_fail (object != NULL);
523   g_return_if_fail (GTK_IS_OBJECT (object));
524
525   weaks = gtk_object_get_data (object, weakrefs_key);
526   for (wp = &weaks; *wp; wp = &(*wp)->next)
527     {
528       w = *wp;
529       if (w->notify == notify && w->data == data)
530         {
531           if (w == weaks)
532             gtk_object_set_data_full (object, weakrefs_key, w->next,
533                                       gtk_object_notify_weaks);
534           else
535             *wp = w->next;
536           g_free (w);
537           return;
538         }
539     }
540 }
541
542 static void
543 gtk_object_notify_weaks (gpointer data)
544 {
545   GtkWeakRef *w1, *w2;
546
547   w1 = (GtkWeakRef *)data;
548
549   while (w1)
550     {
551       w1->notify (w1->data);
552       w2 = w1->next;
553       g_free (w1);
554       w1 = w2;
555     }
556 }
557
558 /*****************************************
559  * gtk_object_new:
560  *
561  *   arguments:
562  *
563  *   results:
564  *****************************************/
565
566 GtkObject*
567 gtk_object_new (guint type,
568                 ...)
569 {
570   GtkObject *obj;
571   GtkArg *args;
572   guint nargs;
573   va_list args1;
574   va_list args2;
575
576   obj = gtk_type_new (type);
577
578   va_start (args1, type);
579   va_start (args2, type);
580
581   args = gtk_object_collect_args (&nargs, args1, args2);
582   gtk_object_setv (obj, nargs, args);
583   g_free (args);
584
585   va_end (args1);
586   va_end (args2);
587
588   return obj;
589 }
590
591 /*****************************************
592  * gtk_object_newv:
593  *
594  *   arguments:
595  *
596  *   results:
597  *****************************************/
598
599 GtkObject*
600 gtk_object_newv (guint   type,
601                  guint    nargs,
602                  GtkArg *args)
603 {
604   gpointer obj;
605
606   obj = gtk_type_new (type);
607   gtk_object_setv (obj, nargs, args);
608
609   return obj;
610 }
611
612 /*****************************************
613  * gtk_object_getv:
614  *
615  *   arguments:
616  *
617  *   results:
618  *****************************************/
619
620 void
621 gtk_object_getv (GtkObject           *object,
622                  guint               nargs,
623                  GtkArg              *args)
624 {
625   int i;
626   
627   g_return_if_fail (object != NULL);
628   
629   if (!arg_info_ht)
630     return;
631   
632   for (i = 0; i < nargs; i++)
633     {
634       GtkArgInfo *info;
635       gchar *lookup_name;
636       gchar *d;
637       
638       
639       /* hm, the name cutting shouldn't be needed on gets, but what the heck...
640        */
641       lookup_name = g_strdup (args[i].name);
642       d = strchr (lookup_name, ':');
643       if (d && d[1] == ':')
644         {
645           d = strchr (d + 2, ':');
646           if (d)
647             *d = 0;
648           
649           info = g_hash_table_lookup (arg_info_ht, lookup_name);
650         }
651       else
652         info = NULL;
653       
654       if (!info)
655         {
656           g_warning ("invalid arg name: \"%s\"\n", lookup_name);
657           args[i].type = GTK_TYPE_INVALID;
658           g_free (lookup_name);
659           continue;
660         }
661       else if (!gtk_type_is_a (object->klass->type, info->class_type))
662         {
663           g_warning ("invalid arg for %s: \"%s\"\n", gtk_type_name (object->klass->type), lookup_name);
664           args[i].type = GTK_TYPE_INVALID;
665           g_free (lookup_name);
666           continue;
667         }
668       else if (!info->arg_flags & GTK_ARG_READABLE)
669         {
670           g_warning ("arg is not supplied for read-access: \"%s\"\n", lookup_name);
671           args[i].type = GTK_TYPE_INVALID;
672           g_free (lookup_name);
673           continue;
674         }
675       else
676         g_free (lookup_name);
677
678       args[i].type = info->type;
679       gtk_type_get_arg (object, info->class_type, &args[i], info->arg_id);
680     }
681 }
682
683 /*****************************************
684  * gtk_object_query_args:
685  *
686  *   arguments:
687  *
688  *   results:
689  *****************************************/
690
691 struct _GtkQueryArgData
692 {
693   GList *arg_list;
694   GtkType class_type;
695 };
696 typedef struct  _GtkQueryArgData        GtkQueryArgData;
697
698 static void
699 gtk_query_arg_foreach (gpointer key,
700                        gpointer value,
701                        gpointer user_data)
702 {
703   register GtkArgInfo *info;
704   register GtkQueryArgData *data;
705
706   info = value;
707   data = user_data;
708
709   if (info->class_type == data->class_type)
710     data->arg_list = g_list_prepend (data->arg_list, info);
711 }
712
713 GtkArg*
714 gtk_object_query_args (GtkType  class_type,
715                        guint32  **arg_flags,
716                        guint    *nargs)
717 {
718   GtkArg *args;
719   GtkQueryArgData query_data;
720
721   if (arg_flags)
722     *arg_flags = NULL;
723   g_return_val_if_fail (nargs != NULL, NULL);
724   *nargs = 0;
725   g_return_val_if_fail (gtk_type_is_a (class_type, gtk_object_get_type ()), NULL);
726
727   if (!arg_info_ht)
728     return NULL;
729
730   /* make sure the types class has been initialized, because
731    * the argument setup happens in the gtk_*_class_init() functions.
732    */
733   gtk_type_class (class_type);
734
735   query_data.arg_list = NULL;
736   query_data.class_type = class_type;
737   g_hash_table_foreach (arg_info_ht, gtk_query_arg_foreach, &query_data);
738
739   if (query_data.arg_list)
740     {
741       register GList    *list;
742       register guint    len;
743
744       list = query_data.arg_list;
745       len = 1;
746       while (list->next)
747         {
748           len++;
749           list = list->next;
750         }
751       g_assert (len == ((GtkObjectClass*) gtk_type_class (class_type))->n_args); /* paranoid */
752
753       args = g_new0 (GtkArg, len);
754       *nargs = len;
755       if (arg_flags)
756         *arg_flags = g_new (guint32, len);
757
758       do
759         {
760           GtkArgInfo *info;
761
762           info = list->data;
763           list = list->prev;
764
765           g_assert (info->seq_id > 0 && info->seq_id <= len); /* paranoid */
766
767           args[info->seq_id - 1].type = info->type;
768           args[info->seq_id - 1].name = info->name;
769           if (arg_flags)
770             (*arg_flags)[info->seq_id - 1] = info->arg_flags;
771         }
772       while (list);
773
774       g_list_free (query_data.arg_list);
775     }
776   else
777     args = NULL;
778
779   return args;
780 }
781
782 /*****************************************
783  * gtk_object_set:
784  *
785  *   arguments:
786  *
787  *   results:
788  *****************************************/
789
790 void
791 gtk_object_set (GtkObject *object,
792                 ...)
793 {
794   GtkArg *args;
795   guint nargs;
796   va_list args1;
797   va_list args2;
798
799   g_return_if_fail (object != NULL);
800
801   va_start (args1, object);
802   va_start (args2, object);
803
804   args = gtk_object_collect_args (&nargs, args1, args2);
805   gtk_object_setv (object, nargs, args);
806   g_free (args);
807
808   va_end (args1);
809   va_end (args2);
810 }
811
812 /*****************************************
813  * gtk_object_setv:
814  *
815  *   arguments:
816  *
817  *   results:
818  *****************************************/
819
820 void
821 gtk_object_setv (GtkObject *object,
822                  guint      nargs,
823                  GtkArg    *args)
824 {
825   int i;
826
827   g_return_if_fail (object != NULL);
828
829   if (!arg_info_ht)
830     return;
831
832   for (i = 0; i < nargs; i++)
833     {
834       GtkArgInfo *info;
835       gchar *lookup_name;
836       gchar *d;
837       gboolean arg_ok;
838
839       lookup_name = g_strdup (args[i].name);
840       d = strchr (lookup_name, ':');
841       if (d && d[1] == ':')
842         {
843           d = strchr (d + 2, ':');
844           if (d)
845             *d = 0;
846
847           info = g_hash_table_lookup (arg_info_ht, lookup_name);
848         }
849       else
850         info = NULL;
851
852       arg_ok = TRUE;
853       
854       if (!info)
855         {
856           g_warning ("invalid arg name: \"%s\"\n", lookup_name);
857           arg_ok = FALSE;
858         }
859       else if (info->type != args[i].type)
860         {
861           g_warning ("invalid arg type for: \"%s\"\n", lookup_name);
862           arg_ok = FALSE;
863         }
864       else if (!gtk_type_is_a (object->klass->type, info->class_type))
865         {
866           g_warning ("invalid arg for %s: \"%s\"\n", gtk_type_name (object->klass->type), lookup_name);
867           arg_ok = FALSE;
868         }
869       else if (!info->arg_flags & GTK_ARG_WRITABLE)
870         {
871           g_warning ("arg is not supplied for write-access: \"%s\"\n", lookup_name);
872           arg_ok = FALSE;
873         }
874       
875       g_free (lookup_name);
876
877       if (!arg_ok)
878         continue;
879
880       gtk_type_set_arg (object, info->class_type, &args[i], info->arg_id);
881     }
882 }
883
884 /*****************************************
885  * gtk_object_add_arg_type:
886  *
887  *   arguments:
888  *
889  *   results:
890  *****************************************/
891
892 void
893 gtk_object_add_arg_type (const char *arg_name,
894                          GtkType     arg_type,
895                          guint       arg_flags,
896                          guint       arg_id)
897 {
898   GtkArgInfo *info;
899   gchar class_part[1024];
900   gchar *arg_part;
901   GtkType class_type;
902
903   g_return_if_fail (arg_name != NULL);
904   g_return_if_fail (arg_type > GTK_TYPE_NONE);
905   g_return_if_fail (arg_id > 0);
906   g_return_if_fail ((arg_flags & GTK_ARG_READWRITE) != 0);
907   
908   arg_part = strchr (arg_name, ':');
909   if (!arg_part || (arg_part[0] != ':') || (arg_part[1] != ':'))
910     {
911       g_warning ("invalid arg name: \"%s\"\n", arg_name);
912       return;
913     }
914
915   strncpy (class_part, arg_name, (glong) (arg_part - arg_name));
916   class_part[(glong) (arg_part - arg_name)] = '\0';
917
918   class_type = gtk_type_from_name (class_part);
919   if (!class_type)
920     {
921       g_warning ("invalid class name in arg: \"%s\"\n", arg_name);
922       return;
923     }
924
925   info = g_new (GtkArgInfo, 1);
926   info->name = g_strdup (arg_name);
927   info->type = arg_type;
928   info->class_type = class_type;
929   info->arg_flags = arg_flags & (GTK_ARG_READABLE | GTK_ARG_WRITABLE);
930   info->arg_id = arg_id;
931   info->seq_id = ++((GtkObjectClass*) gtk_type_class (class_type))->n_args;
932
933   if (!arg_info_ht)
934     arg_info_ht = g_hash_table_new (g_str_hash, g_str_equal);
935
936   g_hash_table_insert (arg_info_ht, info->name, info);
937 }
938
939 /*****************************************
940  * gtk_object_get_arg_type:
941  *
942  *   arguments:
943  *
944  *   results:
945  *****************************************/
946
947 GtkType
948 gtk_object_get_arg_type (const char *arg_name)
949 {
950   GtkArgInfo *info;
951   char buffer[1024];
952   char *t;
953
954   if (!arg_info_ht)
955     return GTK_TYPE_INVALID;
956
957   t = strchr (arg_name, ':');
958   if (!t || (t[0] != ':') || (t[1] != ':'))
959     {
960       g_warning ("invalid arg name: \"%s\"\n", arg_name);
961       return GTK_TYPE_INVALID;
962     }
963
964   t = strchr (t + 2, ':');
965   if (t)
966     {
967       strncpy (buffer, arg_name, (long) (t - arg_name));
968       buffer[(long) (t - arg_name)] = '\0';
969       arg_name = buffer;
970     }
971
972   info = g_hash_table_lookup (arg_info_ht, (gpointer) arg_name);
973   if (info)
974     return info->type;
975
976   return GTK_TYPE_INVALID;
977 }
978
979 /*****************************************
980  * gtk_object_set_data:
981  *
982  *   arguments:
983  *
984  *   results:
985  *****************************************/
986
987 void
988 gtk_object_set_data (GtkObject        *object,
989                      const gchar      *key,
990                      gpointer          data)
991 {
992   gtk_object_set_data_full (object, key, data, NULL);
993 }
994
995 /*****************************************
996  * gtk_object_set_data_full:
997  *
998  *   arguments:
999  *
1000  *   results:
1001  *****************************************/
1002
1003 void
1004 gtk_object_set_data_full (GtkObject        *object,
1005                           const gchar      *key,
1006                           gpointer          data,
1007                           GtkDestroyNotify  destroy)
1008 {
1009   GtkObjectData *odata;
1010   GtkObjectData *prev;
1011   guint *id;
1012
1013   g_return_if_fail (object != NULL);
1014   g_return_if_fail (GTK_IS_OBJECT (object));
1015   g_return_if_fail (key != NULL);
1016
1017   if (object_data_init)
1018     gtk_object_data_init ();
1019
1020   id = g_hash_table_lookup (object_data_ht, (gpointer) key);
1021
1022   if (!data)
1023     {
1024       if (id)
1025         {
1026           prev = NULL;
1027           odata = object->object_data;
1028
1029           while (odata)
1030             {
1031               if (odata->id == *id)
1032                 {
1033                   if (prev)
1034                     prev->next = odata->next;
1035                   if (odata == object->object_data)
1036                     object->object_data = odata->next;
1037
1038                   gtk_object_data_destroy (odata);
1039                   break;
1040                 }
1041
1042               prev = odata;
1043               odata = odata->next;
1044             }
1045         }
1046     }
1047   else
1048     {
1049       if (!id)
1050         {
1051           id = gtk_object_data_id_alloc ();
1052           g_hash_table_insert (object_data_ht, (gpointer) key, id);
1053         }
1054
1055       odata = object->object_data;
1056       while (odata)
1057         {
1058           if (odata->id == *id)
1059             {
1060               odata->data = data;
1061               odata->destroy = destroy;
1062               return;
1063             }
1064
1065           odata = odata->next;
1066         }
1067
1068       odata = gtk_object_data_new ();
1069       odata->id = *id;
1070       odata->data = data;
1071       odata->destroy = destroy;
1072
1073       odata->next = object->object_data;
1074       object->object_data = odata;
1075     }
1076 }
1077
1078 /*****************************************
1079  * gtk_object_get_data:
1080  *
1081  *   arguments:
1082  *
1083  *   results:
1084  *****************************************/
1085
1086 gpointer
1087 gtk_object_get_data (GtkObject   *object,
1088                      const gchar *key)
1089 {
1090   GtkObjectData *odata;
1091   guint *id;
1092
1093   g_return_val_if_fail (object != NULL, NULL);
1094   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
1095   g_return_val_if_fail (key != NULL, NULL);
1096
1097   if (object_data_init)
1098     gtk_object_data_init ();
1099
1100   id = g_hash_table_lookup (object_data_ht, (gpointer) key);
1101   if (id)
1102     {
1103       odata = object->object_data;
1104       while (odata)
1105         {
1106           if (odata->id == *id)
1107             return odata->data;
1108           odata = odata->next;
1109         }
1110     }
1111
1112   return NULL;
1113 }
1114
1115 /*****************************************
1116  * gtk_object_remove_data:
1117  *
1118  *   arguments:
1119  *
1120  *   results:
1121  *****************************************/
1122
1123 void
1124 gtk_object_remove_data (GtkObject   *object,
1125                         const gchar *key)
1126 {
1127   g_return_if_fail (object != NULL);
1128   g_return_if_fail (GTK_IS_OBJECT (object));
1129   g_return_if_fail (key != NULL);
1130
1131   gtk_object_set_data_full (object, key, NULL, NULL);
1132 }
1133
1134 /*****************************************
1135  * gtk_object_set_user_data:
1136  *
1137  *   arguments:
1138  *
1139  *   results:
1140  *****************************************/
1141
1142 void
1143 gtk_object_set_user_data (GtkObject *object,
1144                           gpointer   data)
1145 {
1146   g_return_if_fail (object != NULL);
1147   g_return_if_fail (GTK_IS_OBJECT (object));
1148
1149   gtk_object_set_data_full (object, user_data_key, data, NULL);
1150 }
1151
1152 /*****************************************
1153  * gtk_object_get_user_data:
1154  *
1155  *   arguments:
1156  *
1157  *   results:
1158  *****************************************/
1159
1160 gpointer
1161 gtk_object_get_user_data (GtkObject *object)
1162 {
1163   g_return_val_if_fail (object != NULL, NULL);
1164   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
1165
1166   return gtk_object_get_data (object, user_data_key);
1167 }
1168
1169 /*****************************************
1170  * gtk_object_check_cast:
1171  *
1172  *   arguments:
1173  *
1174  *   results:
1175  *****************************************/
1176
1177 static gchar*
1178 gtk_object_descriptive_type_name (GtkType type)
1179 {
1180   gchar *name;
1181
1182   name = gtk_type_name (type);
1183   if (!name)
1184     name = "(unknown)";
1185
1186   return name;
1187 }
1188
1189 GtkObject*
1190 gtk_object_check_cast (GtkObject *obj,
1191                        GtkType    cast_type)
1192 {
1193   if (!obj)
1194     {
1195       g_warning ("invalid cast from (NULL) pointer to `%s'",
1196                  gtk_object_descriptive_type_name (cast_type));
1197       return obj;
1198     }
1199   if (!obj->klass)
1200     {
1201       g_warning ("invalid unclassed pointer in cast to `%s'",
1202                  gtk_object_descriptive_type_name (cast_type));
1203       return obj;
1204     }
1205   if (obj->klass->type < GTK_TYPE_OBJECT)
1206     {
1207       g_warning ("invalid class type `%s' in cast to `%s'",
1208                  gtk_object_descriptive_type_name (obj->klass->type),
1209                  gtk_object_descriptive_type_name (cast_type));
1210       return obj;
1211     }
1212   if (!gtk_type_is_a (obj->klass->type, cast_type))
1213     {
1214       g_warning ("invalid cast from `%s' to `%s'",
1215                  gtk_object_descriptive_type_name (obj->klass->type),
1216                  gtk_object_descriptive_type_name (cast_type));
1217       return obj;
1218     }
1219   
1220   return obj;
1221 }
1222
1223 /*****************************************
1224  * gtk_object_check_class_cast:
1225  *
1226  *   arguments:
1227  *
1228  *   results:
1229  *****************************************/
1230
1231 GtkObjectClass*
1232 gtk_object_check_class_cast (GtkObjectClass *klass,
1233                              GtkType         cast_type)
1234 {
1235   if (!klass)
1236     {
1237       g_warning ("invalid class cast from (NULL) pointer to `%s'",
1238                  gtk_object_descriptive_type_name (cast_type));
1239       return klass;
1240     }
1241   if (klass->type < GTK_TYPE_OBJECT)
1242     {
1243       g_warning ("invalid class type `%s' in class cast to `%s'",
1244                  gtk_object_descriptive_type_name (klass->type),
1245                  gtk_object_descriptive_type_name (cast_type));
1246       return klass;
1247     }
1248   if (!gtk_type_is_a (klass->type, cast_type))
1249     {
1250       g_warning ("invalid class cast from `%s' to `%s'",
1251                  gtk_object_descriptive_type_name (klass->type),
1252                  gtk_object_descriptive_type_name (cast_type));
1253       return klass;
1254     }
1255
1256   return klass;
1257 }
1258
1259 /*****************************************
1260  * gtk_object_data_init:
1261  *
1262  *   arguments:
1263  *
1264  *   results:
1265  *****************************************/
1266
1267 static void
1268 gtk_object_data_init ()
1269 {
1270   if (object_data_init)
1271     {
1272       object_data_init = FALSE;
1273
1274       object_data_ht = g_hash_table_new (g_str_hash, g_str_equal);
1275     }
1276 }
1277
1278 /*****************************************
1279  * gtk_object_data_new:
1280  *
1281  *   arguments:
1282  *
1283  *   results:
1284  *****************************************/
1285
1286 static GtkObjectData*
1287 gtk_object_data_new ()
1288 {
1289   GtkObjectData *odata;
1290
1291   if (!object_data_mem_chunk)
1292     object_data_mem_chunk = g_mem_chunk_new ("object data mem chunk",
1293                                              sizeof (GtkObjectData),
1294                                              1024, G_ALLOC_AND_FREE);
1295
1296   odata = g_chunk_new (GtkObjectData, object_data_mem_chunk);
1297
1298   odata->id = 0;
1299   odata->data = NULL;
1300   odata->destroy = NULL;
1301   odata->next = NULL;
1302
1303   return odata;
1304 }
1305
1306 /*****************************************
1307  * gtk_object_data_destroy:
1308  *
1309  *   arguments:
1310  *
1311  *   results:
1312  *****************************************/
1313
1314 static void
1315 gtk_object_data_destroy (GtkObjectData *odata)
1316 {
1317   g_return_if_fail (odata != NULL);
1318
1319   if (odata->destroy)
1320     odata->destroy (odata->data);
1321
1322   g_mem_chunk_free (object_data_mem_chunk, odata);
1323 }
1324
1325 /*****************************************
1326  * gtk_object_data_id_alloc:
1327  *
1328  *   arguments:
1329  *
1330  *   results:
1331  *****************************************/
1332
1333 static guint*
1334 gtk_object_data_id_alloc ()
1335 {
1336   static guint next_id = 1;
1337   guint *ids;
1338
1339   if (!object_data_id_list ||
1340       (object_data_id_index == OBJECT_DATA_ID_CHUNK))
1341     {
1342       ids = g_new (guint, OBJECT_DATA_ID_CHUNK);
1343       object_data_id_index = 0;
1344       object_data_id_list = g_slist_prepend (object_data_id_list, ids);
1345     }
1346   else
1347     {
1348       ids = object_data_id_list->data;
1349     }
1350
1351   ids[object_data_id_index] = next_id++;
1352   return &ids[object_data_id_index++];
1353 }
1354
1355 /*****************************************
1356  * gtk_object_collect_args:
1357  *
1358  *   arguments:
1359  *
1360  *   results:
1361  *****************************************/
1362
1363 GtkArg*
1364 gtk_object_collect_args (guint   *nargs,
1365                          va_list  args1,
1366                          va_list  args2)
1367 {
1368   GtkArg *args;
1369   GtkType type;
1370   gchar *name;
1371   gint done;
1372   gint i, n;
1373
1374   n = 0;
1375   done = FALSE;
1376
1377   while (!done)
1378     {
1379       name = va_arg (args1, char *);
1380       if (!name)
1381         {
1382           done = TRUE;
1383           continue;
1384         }
1385
1386       type = gtk_object_get_arg_type (name);
1387
1388       switch (GTK_FUNDAMENTAL_TYPE (type))
1389         {
1390         case GTK_TYPE_INVALID:
1391           g_warning ("invalid arg name: \"%s\" %x\n", name, type);
1392           (void) va_arg (args1, long);
1393           continue;
1394         case GTK_TYPE_NONE:
1395           break;
1396         case GTK_TYPE_CHAR:
1397         case GTK_TYPE_BOOL:
1398         case GTK_TYPE_INT:
1399         case GTK_TYPE_UINT:
1400         case GTK_TYPE_ENUM:
1401         case GTK_TYPE_FLAGS:
1402           (void) va_arg (args1, gint);
1403           break;
1404         case GTK_TYPE_LONG:
1405         case GTK_TYPE_ULONG:
1406           (void) va_arg (args1, glong);
1407           break;
1408         case GTK_TYPE_FLOAT:
1409           (void) va_arg (args1, gfloat);
1410           break;
1411         case GTK_TYPE_DOUBLE:
1412           (void) va_arg (args1, gdouble);
1413           break;
1414         case GTK_TYPE_STRING:
1415           (void) va_arg (args1, gchar*);
1416           break;
1417         case GTK_TYPE_POINTER:
1418         case GTK_TYPE_BOXED:
1419           (void) va_arg (args1, gpointer);
1420           break;
1421         case GTK_TYPE_SIGNAL:
1422           (void) va_arg (args1, GtkFunction);
1423           (void) va_arg (args1, gpointer);
1424           break;
1425         case GTK_TYPE_FOREIGN:
1426           (void) va_arg (args1, gpointer);
1427           (void) va_arg (args1, GtkDestroyNotify);
1428           break;
1429         case GTK_TYPE_CALLBACK:
1430           (void) va_arg (args1, GtkCallbackMarshal);
1431           (void) va_arg (args1, gpointer);
1432           (void) va_arg (args1, GtkDestroyNotify);
1433           break;
1434         case GTK_TYPE_C_CALLBACK:
1435           (void) va_arg (args1, GtkFunction);
1436           (void) va_arg (args1, gpointer);
1437           break;
1438         case GTK_TYPE_ARGS:
1439           (void) va_arg (args1, gint);
1440           (void) va_arg (args1, GtkArg*);
1441           break;
1442         case GTK_TYPE_OBJECT:
1443           (void) va_arg (args1, GtkObject*);
1444           break;
1445         default:
1446           g_error ("unsupported type %s in args", gtk_type_name (type));
1447           break;
1448         }
1449
1450       n += 1;
1451     }
1452
1453   *nargs = n;
1454   args = NULL;
1455
1456   if (n > 0)
1457     {
1458       args = g_new0 (GtkArg, n);
1459
1460       for (i = 0; i < n; i++)
1461         {
1462           args[i].name = va_arg (args2, char *);
1463           args[i].type = gtk_object_get_arg_type (args[i].name);
1464
1465           switch (GTK_FUNDAMENTAL_TYPE (args[i].type))
1466             {
1467             case GTK_TYPE_INVALID:
1468               (void) va_arg (args2, long);
1469               i -= 1;
1470               continue;
1471             case GTK_TYPE_NONE:
1472               break;
1473             case GTK_TYPE_CHAR:
1474               GTK_VALUE_CHAR(args[i]) = va_arg (args2, gint);
1475               break;
1476             case GTK_TYPE_BOOL:
1477               GTK_VALUE_BOOL(args[i]) = va_arg (args2, gint);
1478               break;
1479             case GTK_TYPE_INT:
1480               GTK_VALUE_INT(args[i]) = va_arg (args2, gint);
1481               break;
1482             case GTK_TYPE_UINT:
1483               GTK_VALUE_UINT(args[i]) = va_arg (args2, guint);
1484               break;
1485             case GTK_TYPE_ENUM:
1486               GTK_VALUE_ENUM(args[i]) = va_arg (args2, gint);
1487               break;
1488             case GTK_TYPE_FLAGS:
1489               GTK_VALUE_FLAGS(args[i]) = va_arg (args2, gint);
1490               break;
1491             case GTK_TYPE_LONG:
1492               GTK_VALUE_LONG(args[i]) = va_arg (args2, glong);
1493               break;
1494             case GTK_TYPE_ULONG:
1495               GTK_VALUE_ULONG(args[i]) = va_arg (args2, gulong);
1496               break;
1497             case GTK_TYPE_FLOAT:
1498               GTK_VALUE_FLOAT(args[i]) = va_arg (args2, gfloat);
1499               break;
1500             case GTK_TYPE_DOUBLE:
1501               GTK_VALUE_DOUBLE(args[i]) = va_arg (args2, gdouble);
1502               break;
1503             case GTK_TYPE_STRING:
1504               GTK_VALUE_STRING(args[i]) = va_arg (args2, gchar*);
1505               break;
1506             case GTK_TYPE_POINTER:
1507               GTK_VALUE_POINTER(args[i]) = va_arg (args2, gpointer);
1508               break;
1509             case GTK_TYPE_BOXED:
1510               GTK_VALUE_BOXED(args[i]) = va_arg (args2, gpointer);
1511               break;
1512             case GTK_TYPE_SIGNAL:
1513               GTK_VALUE_SIGNAL(args[i]).f = va_arg (args2, GtkFunction);
1514               GTK_VALUE_SIGNAL(args[i]).d = va_arg (args2, gpointer);
1515               break;
1516             case GTK_TYPE_FOREIGN:
1517               GTK_VALUE_FOREIGN(args[i]).data = va_arg (args2, gpointer);
1518               GTK_VALUE_FOREIGN(args[i]).notify =
1519                 va_arg (args2, GtkDestroyNotify);
1520               break;
1521             case GTK_TYPE_CALLBACK:
1522               GTK_VALUE_CALLBACK(args[i]).marshal =
1523                 va_arg (args2, GtkCallbackMarshal);
1524               GTK_VALUE_CALLBACK(args[i]).data = va_arg (args2, gpointer);
1525               GTK_VALUE_CALLBACK(args[i]).notify =
1526                 va_arg (args2, GtkDestroyNotify);
1527               break;
1528             case GTK_TYPE_C_CALLBACK:
1529               GTK_VALUE_C_CALLBACK(args[i]).func = va_arg (args2, GtkFunction);
1530               GTK_VALUE_C_CALLBACK(args[i]).func_data =
1531                 va_arg (args2, gpointer);
1532               break;
1533             case GTK_TYPE_ARGS:
1534               GTK_VALUE_ARGS(args[i]).n_args = va_arg (args2, gint);
1535               GTK_VALUE_ARGS(args[i]).args = va_arg (args2, GtkArg*);
1536               break;
1537             case GTK_TYPE_OBJECT:
1538               GTK_VALUE_OBJECT(args[i]) = va_arg (args2, GtkObject*);
1539               g_assert (GTK_VALUE_OBJECT(args[i]) == NULL ||
1540                         GTK_CHECK_TYPE (GTK_VALUE_OBJECT(args[i]),
1541                                         args[i].type));
1542               break;
1543             default:
1544               g_error ("unsupported type %s in args",
1545                        gtk_type_name (args[i].type));
1546               break;
1547             }
1548         }
1549     }
1550
1551   return args;
1552 }
1553
1554
1555
1556 #undef  gtk_object_ref
1557 #undef  gtk_object_unref
1558
1559 void
1560 gtk_object_ref (GtkObject *object)
1561 {
1562   g_return_if_fail (object != NULL);
1563   g_return_if_fail (GTK_IS_OBJECT (object));
1564
1565   object->ref_count += 1;
1566 }
1567
1568 void
1569 gtk_object_unref (GtkObject *object)
1570 {
1571   g_return_if_fail (object != NULL);
1572   g_return_if_fail (GTK_IS_OBJECT (object));
1573   
1574   if (object->ref_count == 1)
1575     gtk_object_destroy (object);
1576   
1577   if (object->ref_count > 0)
1578     object->ref_count -= 1;
1579
1580   if (object->ref_count == 0)
1581     {
1582 #ifdef G_ENABLE_DEBUG
1583       if (gtk_debug_flags & GTK_DEBUG_OBJECTS)
1584         {
1585           g_assert (g_slist_find (living_objs, object));
1586           living_objs = g_slist_remove (living_objs, object);
1587           obj_count--;
1588         }
1589 #endif /* G_ENABLE_DEBUG */      
1590       object->klass->finalize (object);
1591     }
1592 }
1593
1594 #ifdef G_ENABLE_DEBUG
1595
1596 static GtkObject *gtk_trace_object = NULL;
1597
1598 void
1599 gtk_trace_referencing (gpointer    *o,
1600                        const gchar *func,
1601                        guint       local_frame,
1602                        guint       line,
1603                        gboolean    do_ref)
1604 {
1605   gboolean exists;
1606   GtkObject *object = (GtkObject*) o;
1607
1608   if (gtk_debug_flags & GTK_DEBUG_OBJECTS)
1609     {
1610       g_return_if_fail (object != NULL);
1611       g_return_if_fail (GTK_IS_OBJECT (object));
1612
1613       exists = (g_slist_find (living_objs, object) != NULL);
1614       
1615       if (exists &&
1616           (object == gtk_trace_object ||
1617            gtk_trace_object == (void*)42))
1618         printf ("trace: object_%s: (%s:%p)->ref_count=%d%s (%s_f%02d:%d)\n",
1619                 do_ref ? "ref" : "unref",
1620                 gtk_type_name (GTK_OBJECT_TYPE (object)),
1621                 object,
1622                 object->ref_count,
1623                 do_ref ? " + 1" : " - 1 ",
1624                 func,
1625                 local_frame,
1626                 line);
1627   
1628       if (!exists)
1629         printf ("trace: object_%s(%p): no such object! (%s_f%02d:%d)\n",
1630                 do_ref ? "ref" : "unref",
1631                 object,
1632                 func,
1633                 local_frame,
1634                 line);
1635     }
1636   
1637   if (do_ref)
1638     gtk_object_ref (object);
1639   else
1640     gtk_object_unref (object);
1641 }
1642
1643 #endif /* G_ENABLE_DEBUG */
1644