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