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