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