]> Pileus Git - ~andy/gtk/blob - gtk/gtkobject.c
Try to guess if we can use the mb* functions safely. (For glibc, they do
[~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 ("GTK-DEBUG: %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 ("GTK-DEBUG: 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 (guint, 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   guint i;
597   
598   g_return_if_fail (object != NULL);
599   g_return_if_fail (GTK_IS_OBJECT (object));
600   
601   if (!arg_info_ht)
602     return;
603   
604   for (i = 0; i < nargs; i++)
605     {
606       GtkArgInfo *info;
607       gchar *lookup_name;
608       gchar *d;
609       
610       
611       /* hm, the name cutting shouldn't be needed on gets, but what the heck...
612        */
613       lookup_name = g_strdup (args[i].name);
614       d = strchr (lookup_name, ':');
615       if (d && d[1] == ':')
616         {
617           d = strchr (d + 2, ':');
618           if (d)
619             *d = 0;
620           
621           info = g_hash_table_lookup (arg_info_ht, lookup_name);
622         }
623       else
624         info = NULL;
625       
626       if (!info)
627         {
628           g_warning ("invalid arg name: \"%s\"\n", lookup_name);
629           args[i].type = GTK_TYPE_INVALID;
630           g_free (lookup_name);
631           continue;
632         }
633       else if (!gtk_type_is_a (object->klass->type, info->class_type))
634         {
635           g_warning ("invalid arg for %s: \"%s\"\n", gtk_type_name (object->klass->type), lookup_name);
636           args[i].type = GTK_TYPE_INVALID;
637           g_free (lookup_name);
638           continue;
639         }
640       else if (! (info->arg_flags & GTK_ARG_READABLE))
641         {
642           g_warning ("arg is not supplied for read-access: \"%s\"\n", lookup_name);
643           args[i].type = GTK_TYPE_INVALID;
644           g_free (lookup_name);
645           continue;
646         }
647       else
648         g_free (lookup_name);
649
650       args[i].type = info->type;
651       gtk_type_get_arg (object, info->class_type, &args[i], info->arg_id);
652     }
653 }
654
655 /*****************************************
656  * gtk_object_query_args:
657  *
658  *   arguments:
659  *
660  *   results:
661  *****************************************/
662
663 struct _GtkQueryArgData
664 {
665   GList *arg_list;
666   GtkType class_type;
667 };
668 typedef struct  _GtkQueryArgData        GtkQueryArgData;
669
670 static void
671 gtk_query_arg_foreach (gpointer key,
672                        gpointer value,
673                        gpointer user_data)
674 {
675   register GtkArgInfo *info;
676   register GtkQueryArgData *data;
677
678   info = value;
679   data = user_data;
680
681   if (info->class_type == data->class_type)
682     data->arg_list = g_list_prepend (data->arg_list, info);
683 }
684
685 GtkArg*
686 gtk_object_query_args (GtkType  class_type,
687                        guint32  **arg_flags,
688                        guint    *nargs)
689 {
690   GtkArg *args;
691   GtkQueryArgData query_data;
692
693   if (arg_flags)
694     *arg_flags = NULL;
695   g_return_val_if_fail (nargs != NULL, NULL);
696   *nargs = 0;
697   g_return_val_if_fail (gtk_type_is_a (class_type, gtk_object_get_type ()), NULL);
698
699   if (!arg_info_ht)
700     return NULL;
701
702   /* make sure the types class has been initialized, because
703    * the argument setup happens in the gtk_*_class_init() functions.
704    */
705   gtk_type_class (class_type);
706
707   query_data.arg_list = NULL;
708   query_data.class_type = class_type;
709   g_hash_table_foreach (arg_info_ht, gtk_query_arg_foreach, &query_data);
710
711   if (query_data.arg_list)
712     {
713       register GList    *list;
714       register guint    len;
715
716       list = query_data.arg_list;
717       len = 1;
718       while (list->next)
719         {
720           len++;
721           list = list->next;
722         }
723       g_assert (len == ((GtkObjectClass*) gtk_type_class (class_type))->n_args); /* paranoid */
724
725       args = g_new0 (GtkArg, len);
726       *nargs = len;
727       if (arg_flags)
728         *arg_flags = g_new (guint32, len);
729
730       do
731         {
732           GtkArgInfo *info;
733
734           info = list->data;
735           list = list->prev;
736
737           g_assert (info->seq_id > 0 && info->seq_id <= len); /* paranoid */
738
739           args[info->seq_id - 1].type = info->type;
740           args[info->seq_id - 1].name = info->name;
741           if (arg_flags)
742             (*arg_flags)[info->seq_id - 1] = info->arg_flags;
743         }
744       while (list);
745
746       g_list_free (query_data.arg_list);
747     }
748   else
749     args = NULL;
750
751   return args;
752 }
753
754 /*****************************************
755  * gtk_object_set:
756  *
757  *   arguments:
758  *
759  *   results:
760  *****************************************/
761
762 void
763 gtk_object_set (GtkObject *object,
764                 ...)
765 {
766   GtkArg *args;
767   guint nargs;
768   va_list args1;
769   va_list args2;
770
771   g_return_if_fail (object != NULL);
772
773   va_start (args1, object);
774   va_start (args2, object);
775
776   args = gtk_object_collect_args (&nargs, args1, args2);
777   gtk_object_setv (object, nargs, args);
778   g_free (args);
779
780   va_end (args1);
781   va_end (args2);
782 }
783
784 /*****************************************
785  * gtk_object_setv:
786  *
787  *   arguments:
788  *
789  *   results:
790  *****************************************/
791
792 void
793 gtk_object_setv (GtkObject *object,
794                  guint      nargs,
795                  GtkArg    *args)
796 {
797   guint i;
798
799   g_return_if_fail (object != NULL);
800   g_return_if_fail (GTK_OBJECT (object));
801
802   if (!arg_info_ht)
803     return;
804
805   for (i = 0; i < nargs; i++)
806     {
807       GtkArgInfo *info;
808       gchar *lookup_name;
809       gchar *d;
810       gboolean arg_ok;
811
812       lookup_name = g_strdup (args[i].name);
813       d = strchr (lookup_name, ':');
814       if (d && d[1] == ':')
815         {
816           d = strchr (d + 2, ':');
817           if (d)
818             *d = 0;
819
820           info = g_hash_table_lookup (arg_info_ht, lookup_name);
821         }
822       else
823         info = NULL;
824
825       arg_ok = TRUE;
826       
827       if (!info)
828         {
829           g_warning ("invalid arg name: \"%s\"\n", lookup_name);
830           arg_ok = FALSE;
831         }
832       else if (info->type != args[i].type)
833         {
834           g_warning ("invalid arg type for: \"%s\"\n", lookup_name);
835           arg_ok = FALSE;
836         }
837       else if (!gtk_type_is_a (object->klass->type, info->class_type))
838         {
839           g_warning ("invalid arg for %s: \"%s\"\n", gtk_type_name (object->klass->type), lookup_name);
840           arg_ok = FALSE;
841         }
842       else if (! (info->arg_flags & GTK_ARG_WRITABLE))
843         {
844           g_warning ("arg is not supplied for write-access: \"%s\"\n", lookup_name);
845           arg_ok = FALSE;
846         }
847       
848       g_free (lookup_name);
849
850       if (!arg_ok)
851         continue;
852
853       gtk_type_set_arg (object, info->class_type, &args[i], info->arg_id);
854     }
855 }
856
857 /*****************************************
858  * gtk_object_add_arg_type:
859  *
860  *   arguments:
861  *
862  *   results:
863  *****************************************/
864
865 void
866 gtk_object_add_arg_type (const char *arg_name,
867                          GtkType     arg_type,
868                          guint       arg_flags,
869                          guint       arg_id)
870 {
871   GtkArgInfo *info;
872   gchar class_part[1024];
873   gchar *arg_part;
874   GtkType class_type;
875
876   g_return_if_fail (arg_name != NULL);
877   g_return_if_fail (arg_type > GTK_TYPE_NONE);
878   g_return_if_fail (arg_id > 0);
879   g_return_if_fail ((arg_flags & GTK_ARG_READWRITE) != 0);
880   
881   arg_part = strchr (arg_name, ':');
882   if (!arg_part || (arg_part[0] != ':') || (arg_part[1] != ':'))
883     {
884       g_warning ("invalid arg name: \"%s\"\n", arg_name);
885       return;
886     }
887
888   strncpy (class_part, arg_name, (glong) (arg_part - arg_name));
889   class_part[(glong) (arg_part - arg_name)] = '\0';
890
891   class_type = gtk_type_from_name (class_part);
892   if (!class_type)
893     {
894       g_warning ("invalid class name in arg: \"%s\"\n", arg_name);
895       return;
896     }
897
898   info = g_new (GtkArgInfo, 1);
899   info->name = g_strdup (arg_name);
900   info->type = arg_type;
901   info->class_type = class_type;
902   info->arg_flags = arg_flags & (GTK_ARG_READABLE | GTK_ARG_WRITABLE);
903   info->arg_id = arg_id;
904   info->seq_id = ++((GtkObjectClass*) gtk_type_class (class_type))->n_args;
905
906   if (!arg_info_ht)
907     arg_info_ht = g_hash_table_new (g_str_hash, g_str_equal);
908
909   g_hash_table_insert (arg_info_ht, info->name, info);
910 }
911
912 /*****************************************
913  * gtk_object_get_arg_type:
914  *
915  *   arguments:
916  *
917  *   results:
918  *****************************************/
919
920 GtkType
921 gtk_object_get_arg_type (const gchar *arg_name)
922 {
923   GtkArgInfo *info;
924   gchar buffer[1024];
925   gchar *t;
926
927   if (!arg_info_ht)
928     return GTK_TYPE_INVALID;
929
930   t = strchr (arg_name, ':');
931   if (!t || (t[0] != ':') || (t[1] != ':'))
932     {
933       g_warning ("invalid arg name: \"%s\"\n", arg_name);
934       return GTK_TYPE_INVALID;
935     }
936
937   t = strchr (t + 2, ':');
938   if (t)
939     {
940       strncpy (buffer, arg_name, (long) (t - arg_name));
941       buffer[(long) (t - arg_name)] = '\0';
942       arg_name = buffer;
943     }
944
945   info = g_hash_table_lookup (arg_info_ht, (gpointer) arg_name);
946   if (info)
947     return info->type;
948
949   return GTK_TYPE_INVALID;
950 }
951
952 /*****************************************
953  * GtkObject object_data mechanism
954  *
955  *****************************************/
956
957 void
958 gtk_object_set_data_by_id (GtkObject        *object,
959                            guint             data_id,
960                            gpointer          data)
961 {
962   g_return_if_fail (data_id > 0);
963   
964   gtk_object_set_data_by_id_full (object, data_id, data, NULL);
965 }
966
967 void
968 gtk_object_set_data (GtkObject        *object,
969                      const gchar      *key,
970                      gpointer          data)
971 {
972   g_return_if_fail (key != NULL);
973   
974   gtk_object_set_data_by_id_full (object, gtk_object_data_force_id (key), data, NULL);
975 }
976
977 void
978 gtk_object_set_data_by_id_full (GtkObject        *object,
979                                 guint             data_id,
980                                 gpointer          data,
981                                 GtkDestroyNotify  destroy)
982 {
983   GtkObjectData *odata;
984   GtkObjectData *prev;
985   
986   g_return_if_fail (object != NULL);
987   g_return_if_fail (GTK_IS_OBJECT (object));
988   g_return_if_fail (data_id > 0);
989   
990   if (!data)
991     {
992       prev = NULL;
993       odata = object->object_data;
994       
995       while (odata)
996         {
997           if (odata->id == data_id)
998             {
999               if (prev)
1000                 prev->next = odata->next;
1001               if (odata == object->object_data)
1002                 object->object_data = odata->next;
1003               
1004               gtk_object_data_destroy (odata);
1005               break;
1006             }
1007           
1008           prev = odata;
1009           odata = odata->next;
1010         }
1011     }
1012   else
1013     {
1014       odata = object->object_data;
1015       while (odata)
1016         {
1017           if (odata->id == data_id)
1018             {
1019               if (odata->destroy)
1020                 odata->destroy (odata->data);
1021               
1022               odata->data = data;
1023               odata->destroy = destroy;
1024               return;
1025             }
1026           
1027           odata = odata->next;
1028         }
1029       
1030       if (!object_data_mem_chunk)
1031         object_data_mem_chunk = g_mem_chunk_new ("object data mem chunk",
1032                                                  sizeof (GtkObjectData),
1033                                                  1024, G_ALLOC_AND_FREE);
1034       
1035       odata = g_chunk_new (GtkObjectData, object_data_mem_chunk);
1036       odata->id = data_id;
1037       odata->data = data;
1038       odata->destroy = destroy;
1039       odata->next = object->object_data;
1040       
1041       object->object_data = odata;
1042     }
1043 }
1044
1045 void
1046 gtk_object_set_data_full (GtkObject        *object,
1047                           const gchar      *key,
1048                           gpointer          data,
1049                           GtkDestroyNotify  destroy)
1050 {
1051   g_return_if_fail (key != NULL);
1052
1053   gtk_object_set_data_by_id_full (object, gtk_object_data_force_id (key), data, destroy);
1054 }
1055
1056 guint
1057 gtk_object_data_force_id (const gchar     *key)
1058 {
1059   guint *id;
1060
1061   g_return_val_if_fail (key != NULL, 0);
1062
1063   if (!object_data_ht)
1064     object_data_ht = g_hash_table_new (g_str_hash, g_str_equal);
1065
1066   id = g_hash_table_lookup (object_data_ht, (gpointer) key);
1067   if (!id)
1068     {
1069       id = gtk_object_data_id_alloc ();
1070       g_hash_table_insert (object_data_ht, g_strdup (key), id);
1071     }
1072
1073   return *id;
1074 }
1075
1076 gpointer
1077 gtk_object_get_data_by_id (GtkObject   *object,
1078                            guint        data_id)
1079 {
1080   GtkObjectData *odata;
1081
1082   g_return_val_if_fail (object != NULL, NULL);
1083   g_return_val_if_fail (GTK_IS_OBJECT (object), NULL);
1084
1085   if (data_id)
1086     {
1087       odata = object->object_data;
1088       while (odata)
1089         {
1090           if (odata->id == data_id)
1091             return odata->data;
1092           odata = odata->next;
1093         }
1094     }
1095   
1096   return NULL;
1097 }
1098
1099 gpointer
1100 gtk_object_get_data (GtkObject   *object,
1101                      const gchar *key)
1102 {
1103   guint id;
1104
1105   g_return_val_if_fail (key != NULL, NULL);
1106
1107   id = gtk_object_data_try_key (key);
1108   if (id)
1109     return gtk_object_get_data_by_id (object, id);
1110
1111   return NULL;
1112 }
1113
1114 guint
1115 gtk_object_data_try_key (const gchar     *key)
1116 {
1117   g_return_val_if_fail (key != NULL, 0);
1118
1119   if (object_data_ht)
1120     {
1121       guint *id;
1122
1123       id = g_hash_table_lookup (object_data_ht, (gpointer) key);
1124       if (id)
1125         return *id;
1126     }
1127
1128   return 0;
1129 }
1130
1131 void
1132 gtk_object_remove_data_by_id (GtkObject   *object,
1133                               guint        data_id)
1134 {
1135   if (data_id)
1136     gtk_object_set_data_by_id_full (object, data_id, NULL, NULL);
1137 }
1138
1139 void
1140 gtk_object_remove_data (GtkObject   *object,
1141                         const gchar *key)
1142 {
1143   gint id;
1144
1145   g_return_if_fail (key != NULL);
1146
1147   id = gtk_object_data_try_key (key);
1148   if (id)
1149     gtk_object_set_data_by_id_full (object, id, NULL, NULL);
1150 }
1151
1152 static void
1153 gtk_object_data_destroy (GtkObjectData *odata)
1154 {
1155   g_return_if_fail (odata != NULL);
1156
1157   if (odata->destroy)
1158     odata->destroy (odata->data);
1159
1160   g_mem_chunk_free (object_data_mem_chunk, odata);
1161 }
1162
1163 static guint*
1164 gtk_object_data_id_alloc ()
1165 {
1166   static guint next_id = 1;
1167   guint *ids;
1168
1169   if (!object_data_id_list ||
1170       (object_data_id_index == OBJECT_DATA_ID_CHUNK))
1171     {
1172       ids = g_new (guint, OBJECT_DATA_ID_CHUNK);
1173       object_data_id_index = 0;
1174       object_data_id_list = g_slist_prepend (object_data_id_list, ids);
1175     }
1176   else
1177     {
1178       ids = object_data_id_list->data;
1179     }
1180
1181   ids[object_data_id_index] = next_id++;
1182   return &ids[object_data_id_index++];
1183 }
1184
1185 /*****************************************
1186  * gtk_object_set_user_data:
1187  *
1188  *   arguments:
1189  *
1190  *   results:
1191  *****************************************/
1192
1193 void
1194 gtk_object_set_user_data (GtkObject *object,
1195                           gpointer   data)
1196 {
1197   if (!user_data_key_id)
1198     user_data_key_id = gtk_object_data_force_id (user_data_key);
1199
1200   gtk_object_set_data_by_id_full (object, user_data_key_id, data, NULL);
1201 }
1202
1203 /*****************************************
1204  * gtk_object_get_user_data:
1205  *
1206  *   arguments:
1207  *
1208  *   results:
1209  *****************************************/
1210
1211 gpointer
1212 gtk_object_get_user_data (GtkObject *object)
1213 {
1214   if (user_data_key_id)
1215     return gtk_object_get_data_by_id (object, user_data_key_id);
1216
1217   return NULL;
1218 }
1219
1220 /*****************************************
1221  * gtk_object_check_cast:
1222  *
1223  *   arguments:
1224  *
1225  *   results:
1226  *****************************************/
1227
1228 static gchar*
1229 gtk_object_descriptive_type_name (GtkType type)
1230 {
1231   gchar *name;
1232
1233   name = gtk_type_name (type);
1234   if (!name)
1235     name = "(unknown)";
1236
1237   return name;
1238 }
1239
1240 GtkObject*
1241 gtk_object_check_cast (GtkObject *obj,
1242                        GtkType    cast_type)
1243 {
1244   if (!obj)
1245     {
1246       g_warning ("invalid cast from (NULL) pointer to `%s'",
1247                  gtk_object_descriptive_type_name (cast_type));
1248       return obj;
1249     }
1250   if (!obj->klass)
1251     {
1252       g_warning ("invalid unclassed pointer in cast to `%s'",
1253                  gtk_object_descriptive_type_name (cast_type));
1254       return obj;
1255     }
1256   if (obj->klass->type < GTK_TYPE_OBJECT)
1257     {
1258       g_warning ("invalid class type `%s' in cast to `%s'",
1259                  gtk_object_descriptive_type_name (obj->klass->type),
1260                  gtk_object_descriptive_type_name (cast_type));
1261       return obj;
1262     }
1263   if (!gtk_type_is_a (obj->klass->type, cast_type))
1264     {
1265       g_warning ("invalid cast from `%s' to `%s'",
1266                  gtk_object_descriptive_type_name (obj->klass->type),
1267                  gtk_object_descriptive_type_name (cast_type));
1268       return obj;
1269     }
1270   
1271   return obj;
1272 }
1273
1274 /*****************************************
1275  * gtk_object_check_class_cast:
1276  *
1277  *   arguments:
1278  *
1279  *   results:
1280  *****************************************/
1281
1282 GtkObjectClass*
1283 gtk_object_check_class_cast (GtkObjectClass *klass,
1284                              GtkType         cast_type)
1285 {
1286   if (!klass)
1287     {
1288       g_warning ("invalid class cast from (NULL) pointer to `%s'",
1289                  gtk_object_descriptive_type_name (cast_type));
1290       return klass;
1291     }
1292   if (klass->type < GTK_TYPE_OBJECT)
1293     {
1294       g_warning ("invalid class type `%s' in class cast to `%s'",
1295                  gtk_object_descriptive_type_name (klass->type),
1296                  gtk_object_descriptive_type_name (cast_type));
1297       return klass;
1298     }
1299   if (!gtk_type_is_a (klass->type, cast_type))
1300     {
1301       g_warning ("invalid class cast from `%s' to `%s'",
1302                  gtk_object_descriptive_type_name (klass->type),
1303                  gtk_object_descriptive_type_name (cast_type));
1304       return klass;
1305     }
1306
1307   return klass;
1308 }
1309
1310 /*****************************************
1311  * gtk_object_collect_args:
1312  *
1313  *   arguments:
1314  *
1315  *   results:
1316  *****************************************/
1317
1318 GtkArg*
1319 gtk_object_collect_args (guint   *nargs,
1320                          va_list  args1,
1321                          va_list  args2)
1322 {
1323   GtkArg *args;
1324   GtkType type;
1325   gchar *name;
1326   gint done;
1327   gint i, n;
1328
1329   n = 0;
1330   done = FALSE;
1331
1332   while (!done)
1333     {
1334       name = va_arg (args1, char *);
1335       if (!name)
1336         {
1337           done = TRUE;
1338           continue;
1339         }
1340
1341       type = gtk_object_get_arg_type (name);
1342
1343       switch (GTK_FUNDAMENTAL_TYPE (type))
1344         {
1345         case GTK_TYPE_INVALID:
1346           g_warning ("invalid arg name: \"%s\" %x\n", name, type);
1347           (void) va_arg (args1, long);
1348           continue;
1349         case GTK_TYPE_NONE:
1350           break;
1351         case GTK_TYPE_CHAR:
1352         case GTK_TYPE_BOOL:
1353         case GTK_TYPE_INT:
1354         case GTK_TYPE_UINT:
1355         case GTK_TYPE_ENUM:
1356         case GTK_TYPE_FLAGS:
1357           (void) va_arg (args1, gint);
1358           break;
1359         case GTK_TYPE_LONG:
1360         case GTK_TYPE_ULONG:
1361           (void) va_arg (args1, glong);
1362           break;
1363         case GTK_TYPE_FLOAT:
1364           (void) va_arg (args1, gfloat);
1365           break;
1366         case GTK_TYPE_DOUBLE:
1367           (void) va_arg (args1, gdouble);
1368           break;
1369         case GTK_TYPE_STRING:
1370           (void) va_arg (args1, gchar*);
1371           break;
1372         case GTK_TYPE_POINTER:
1373         case GTK_TYPE_BOXED:
1374           (void) va_arg (args1, gpointer);
1375           break;
1376         case GTK_TYPE_SIGNAL:
1377           (void) va_arg (args1, GtkFunction);
1378           (void) va_arg (args1, gpointer);
1379           break;
1380         case GTK_TYPE_FOREIGN:
1381           (void) va_arg (args1, gpointer);
1382           (void) va_arg (args1, GtkDestroyNotify);
1383           break;
1384         case GTK_TYPE_CALLBACK:
1385           (void) va_arg (args1, GtkCallbackMarshal);
1386           (void) va_arg (args1, gpointer);
1387           (void) va_arg (args1, GtkDestroyNotify);
1388           break;
1389         case GTK_TYPE_C_CALLBACK:
1390           (void) va_arg (args1, GtkFunction);
1391           (void) va_arg (args1, gpointer);
1392           break;
1393         case GTK_TYPE_ARGS:
1394           (void) va_arg (args1, gint);
1395           (void) va_arg (args1, GtkArg*);
1396           break;
1397         case GTK_TYPE_OBJECT:
1398           (void) va_arg (args1, GtkObject*);
1399           break;
1400         default:
1401           g_error ("unsupported type %s in args", gtk_type_name (type));
1402           break;
1403         }
1404
1405       n += 1;
1406     }
1407
1408   *nargs = n;
1409   args = NULL;
1410
1411   if (n > 0)
1412     {
1413       args = g_new0 (GtkArg, n);
1414
1415       for (i = 0; i < n; i++)
1416         {
1417           args[i].name = va_arg (args2, char *);
1418           args[i].type = gtk_object_get_arg_type (args[i].name);
1419
1420           switch (GTK_FUNDAMENTAL_TYPE (args[i].type))
1421             {
1422             case GTK_TYPE_INVALID:
1423               (void) va_arg (args2, long);
1424               i -= 1;
1425               continue;
1426             case GTK_TYPE_NONE:
1427               break;
1428             case GTK_TYPE_CHAR:
1429               GTK_VALUE_CHAR(args[i]) = va_arg (args2, gint);
1430               break;
1431             case GTK_TYPE_BOOL:
1432               GTK_VALUE_BOOL(args[i]) = va_arg (args2, gint);
1433               break;
1434             case GTK_TYPE_INT:
1435               GTK_VALUE_INT(args[i]) = va_arg (args2, gint);
1436               break;
1437             case GTK_TYPE_UINT:
1438               GTK_VALUE_UINT(args[i]) = va_arg (args2, guint);
1439               break;
1440             case GTK_TYPE_ENUM:
1441               GTK_VALUE_ENUM(args[i]) = va_arg (args2, gint);
1442               break;
1443             case GTK_TYPE_FLAGS:
1444               GTK_VALUE_FLAGS(args[i]) = va_arg (args2, gint);
1445               break;
1446             case GTK_TYPE_LONG:
1447               GTK_VALUE_LONG(args[i]) = va_arg (args2, glong);
1448               break;
1449             case GTK_TYPE_ULONG:
1450               GTK_VALUE_ULONG(args[i]) = va_arg (args2, gulong);
1451               break;
1452             case GTK_TYPE_FLOAT:
1453               GTK_VALUE_FLOAT(args[i]) = va_arg (args2, gfloat);
1454               break;
1455             case GTK_TYPE_DOUBLE:
1456               GTK_VALUE_DOUBLE(args[i]) = va_arg (args2, gdouble);
1457               break;
1458             case GTK_TYPE_STRING:
1459               GTK_VALUE_STRING(args[i]) = va_arg (args2, gchar*);
1460               break;
1461             case GTK_TYPE_POINTER:
1462               GTK_VALUE_POINTER(args[i]) = va_arg (args2, gpointer);
1463               break;
1464             case GTK_TYPE_BOXED:
1465               GTK_VALUE_BOXED(args[i]) = va_arg (args2, gpointer);
1466               break;
1467             case GTK_TYPE_SIGNAL:
1468               GTK_VALUE_SIGNAL(args[i]).f = va_arg (args2, GtkFunction);
1469               GTK_VALUE_SIGNAL(args[i]).d = va_arg (args2, gpointer);
1470               break;
1471             case GTK_TYPE_FOREIGN:
1472               GTK_VALUE_FOREIGN(args[i]).data = va_arg (args2, gpointer);
1473               GTK_VALUE_FOREIGN(args[i]).notify =
1474                 va_arg (args2, GtkDestroyNotify);
1475               break;
1476             case GTK_TYPE_CALLBACK:
1477               GTK_VALUE_CALLBACK(args[i]).marshal =
1478                 va_arg (args2, GtkCallbackMarshal);
1479               GTK_VALUE_CALLBACK(args[i]).data = va_arg (args2, gpointer);
1480               GTK_VALUE_CALLBACK(args[i]).notify =
1481                 va_arg (args2, GtkDestroyNotify);
1482               break;
1483             case GTK_TYPE_C_CALLBACK:
1484               GTK_VALUE_C_CALLBACK(args[i]).func = va_arg (args2, GtkFunction);
1485               GTK_VALUE_C_CALLBACK(args[i]).func_data =
1486                 va_arg (args2, gpointer);
1487               break;
1488             case GTK_TYPE_ARGS:
1489               GTK_VALUE_ARGS(args[i]).n_args = va_arg (args2, gint);
1490               GTK_VALUE_ARGS(args[i]).args = va_arg (args2, GtkArg*);
1491               break;
1492             case GTK_TYPE_OBJECT:
1493               GTK_VALUE_OBJECT(args[i]) = va_arg (args2, GtkObject*);
1494               g_assert (GTK_VALUE_OBJECT(args[i]) == NULL ||
1495                         GTK_CHECK_TYPE (GTK_VALUE_OBJECT(args[i]),
1496                                         args[i].type));
1497               break;
1498             default:
1499               g_error ("unsupported type %s in args",
1500                        gtk_type_name (args[i].type));
1501               break;
1502             }
1503         }
1504     }
1505
1506   return args;
1507 }
1508
1509
1510
1511 #undef  gtk_object_ref
1512 #undef  gtk_object_unref
1513
1514 void
1515 gtk_object_ref (GtkObject *object)
1516 {
1517   g_return_if_fail (object != NULL);
1518   g_return_if_fail (GTK_IS_OBJECT (object));
1519
1520   object->ref_count += 1;
1521 }
1522
1523 void
1524 gtk_object_unref (GtkObject *object)
1525 {
1526   g_return_if_fail (object != NULL);
1527   g_return_if_fail (GTK_IS_OBJECT (object));
1528   
1529   if (object->ref_count == 1)
1530     gtk_object_destroy (object);
1531   
1532   if (object->ref_count > 0)
1533     object->ref_count -= 1;
1534
1535   if (object->ref_count == 0)
1536     {
1537 #ifdef G_ENABLE_DEBUG
1538       if (gtk_debug_flags & GTK_DEBUG_OBJECTS)
1539         {
1540           g_assert (g_hash_table_lookup (living_objs_ht, object) == object);
1541           g_hash_table_remove (living_objs_ht, object);
1542           obj_count--;
1543         }
1544 #endif /* G_ENABLE_DEBUG */      
1545       object->klass->finalize (object);
1546     }
1547 }
1548
1549
1550 #ifdef G_ENABLE_DEBUG
1551 static GtkObject *gtk_trace_object = NULL;
1552 void
1553 gtk_trace_referencing (gpointer    *o,
1554                        const gchar *func,
1555                        guint       local_frame,
1556                        guint       line,
1557                        gboolean    do_ref)
1558 {
1559   gboolean exists;
1560   GtkObject *object = (GtkObject*) o;
1561
1562   if (gtk_debug_flags & GTK_DEBUG_OBJECTS)
1563     {
1564       g_return_if_fail (object != NULL);
1565       g_return_if_fail (GTK_IS_OBJECT (object));
1566
1567       exists = g_hash_table_lookup (living_objs_ht, object) != NULL;
1568       
1569       if (exists &&
1570           (object == gtk_trace_object ||
1571            gtk_trace_object == (void*)42))
1572         printf ("trace: object_%s: (%s:%p)->ref_count=%d%s (%s_f%02d:%d)\n",
1573                 do_ref ? "ref" : "unref",
1574                 gtk_type_name (GTK_OBJECT_TYPE (object)),
1575                 object,
1576                 object->ref_count,
1577                 do_ref ? " + 1" : " - 1 ",
1578                 func,
1579                 local_frame,
1580                 line);
1581   
1582       if (!exists)
1583         printf ("trace: object_%s(%p): no such object! (%s_f%02d:%d)\n",
1584                 do_ref ? "ref" : "unref",
1585                 object,
1586                 func,
1587                 local_frame,
1588                 line);
1589     }
1590   
1591   if (do_ref)
1592     gtk_object_ref (object);
1593   else
1594     gtk_object_unref (object);
1595 }
1596 #endif /* G_ENABLE_DEBUG */
1597