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