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