scanobj: use factory methods to get metadata
[platform/upstream/gst-common.git] / gstdoc-scangobj
1 #!/usr/bin/perl -w
2 # -*- cperl -*-
3 #
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998  Damon Chaplin
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #
21
22 #
23 # This gets information about object heirarchies and signals
24 # by compiling a small C program. CFLAGS and LDFLAGS must be
25 # set appropriately before running this script.
26 #
27 # NOTE: the lookup_signal_arg_names() function contains the argument names of
28 #       standard GTK signal handlers. This may need to be updated for new
29 #       GTK signals or Gnome widget signals.
30
31 use Getopt::Long;
32
33 unshift @INC, '/usr/share/gtk-doc/data';
34 require "gtkdoc-common.pl";
35
36 # Options
37
38 # name of documentation module
39 my $MODULE;
40 my $OUTPUT_DIR;
41 my $INSPECT_DIR;
42 my $PRINT_VERSION;
43 my $PRINT_HELP;
44 my $TYPE_INIT_FUNC="g_type_init ()";
45
46 # --nogtkinit is deprecated, as it is the default now anyway.
47 %optctl = (module => \$MODULE,
48            source => \$SOURCE,
49            types => \$TYPES_FILE,
50            nogtkinit => \$NO_GTK_INIT,
51            'type-init-func' => \$TYPE_INIT_FUNC,
52            'output-dir' => \$OUTPUT_DIR,
53            'inspect-dir' => \$INSPECT_DIR,
54            'version' => \$PRINT_VERSION,
55            'help' => \$PRINT_HELP);
56
57 GetOptions(\%optctl, "module=s", "source=s", "types:s", "output-dir:s", "inspect-dir:s", "nogtkinit", "type-init-func:s", "version", "help");
58
59 if ($NO_GTK_INIT) {
60   # Do nothing. This just avoids a warning.
61 }
62
63 if ($PRINT_VERSION) {
64     print "1.5\n";
65     exit 0;
66 }
67
68 if (!$MODULE) {
69     $PRINT_HELP = 1;
70 }
71
72 if ($PRINT_HELP) {
73     print "gstdoc-scangobj version 1.5\n";
74     print "\n--module=MODULE_NAME  Name of the doc module being parsed";
75     print "\n--source=SOURCE_NAME  Name of the source module for plugins";
76     print "\n--types=FILE          The name of the file to store the types in";
77     print "\n--type-init-func=FUNC The init function to call instead of g_type_init ()";
78     print "\n--output-dir=DIRNAME  The directory where the results are stored";
79     print "\n--inspect-dir=DIRNAME  The directory where the plugin inspect data is stored";
80     print "\n--version             Print the version of this program";
81     print "\n--help                Print this help\n";
82     exit 0;
83 }
84
85 $OUTPUT_DIR = $OUTPUT_DIR ? $OUTPUT_DIR : ".";
86
87 $TYPES_FILE = $TYPES_FILE ? $TYPES_FILE : "$OUTPUT_DIR/$MODULE.types";
88
89 open (TYPES, $TYPES_FILE) || die "Cannot open $TYPES_FILE: $!\n";
90 open (OUTPUT, ">$MODULE-scan.c") || die "Cannot open $MODULE-scan.c: $!\n";
91
92 my $old_signals_filename = "$OUTPUT_DIR/$MODULE.signals";
93 my $new_signals_filename = "$OUTPUT_DIR/$MODULE.signals.new";
94 my $old_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy";
95 my $new_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy.new";
96 my $old_interfaces_filename = "$OUTPUT_DIR/$MODULE.interfaces";
97 my $new_interfaces_filename = "$OUTPUT_DIR/$MODULE.interfaces.new";
98 my $old_prerequisites_filename = "$OUTPUT_DIR/$MODULE.prerequisites";
99 my $new_prerequisites_filename = "$OUTPUT_DIR/$MODULE.prerequisites.new";
100 my $old_args_filename = "$OUTPUT_DIR/$MODULE.args";
101 my $new_args_filename = "$OUTPUT_DIR/$MODULE.args.new";
102
103 # write a C program to scan the types
104
105 $includes = "";
106 @types = ();
107 @impl_types = ();
108
109 for (<TYPES>) {
110     if (/^#include/) {
111         $includes .= $_;
112     } elsif (/^%/) {
113         next;
114     } elsif (/^\s*$/) {
115         next;
116     } elsif (/^type:(.*)$/) {
117         $t = $1;
118         chomp $t;
119         push @impl_types, $t;
120     } else {
121         chomp;
122         push @types, $_;
123     }
124 }
125
126 $ntypes = @types + @impl_types;
127
128 print OUTPUT <<EOT;
129 #include <string.h>
130 #include <stdlib.h>
131 #include <stdio.h>
132 #include <errno.h>
133
134 $includes
135
136 #ifdef GTK_IS_WIDGET_CLASS
137 #include <gtk/gtkversion.h>
138 #endif
139
140 static GType *object_types = NULL;
141
142 static GString *xmlstr = NULL;
143
144 static const gchar*
145 xmlprint (gint indent, const gchar *tag, const gchar *data)
146 {
147   const gchar indent_str[] = "                                               ";
148
149   /* reset */
150   g_string_truncate (xmlstr, 0);
151   g_string_append_len (xmlstr, indent_str, MIN (indent, strlen (indent_str)));
152   g_string_append_printf (xmlstr, "<%s>", tag);
153
154   if (data) {
155     gchar *s;
156
157     s = g_markup_escape_text (data, -1);
158     g_string_append (xmlstr, s);
159     g_free (s);
160   }
161   
162   g_string_append_printf (xmlstr, "</%s>\\n", tag);
163   return xmlstr->str;
164 }
165
166 static gint
167 gst_feature_sort_compare (gconstpointer a, gconstpointer b)
168 {
169   return strcmp (((GstPluginFeature *)a)->name, ((GstPluginFeature *)b)->name); 
170 }
171
172 static gint
173 static_pad_template_compare (gconstpointer a, gconstpointer b)
174 {
175   GstStaticPadTemplate *spt_a = (GstStaticPadTemplate *) a;
176   GstStaticPadTemplate *spt_b = (GstStaticPadTemplate *) b;
177
178   /* we want SINK before SRC (enum is UNKNOWN, SRC, SINK) */
179   if (spt_a->direction != spt_b->direction)
180     return spt_b->direction - spt_a->direction;
181
182   /* we want ALWAYS first, SOMETIMES second, REQUEST last
183    * (enum is ALWAYS, SOMETIMES, REQUEST) */
184   if (spt_a->presence != spt_b->presence)
185     return spt_a->presence - spt_b->presence;
186
187   return strcmp (spt_a->name_template, spt_b->name_template);
188 }
189
190 static GType *
191 get_object_types (void)
192 {
193     GList *plugins = NULL;
194     GList *factories = NULL;
195     GList *l;
196     GstElementFactory *factory = NULL;
197     GType type;
198     gint i = 0;
199     gboolean reinspect;
200     
201     /* get a list of features from plugins in our source module */
202     plugins = gst_registry_get_plugin_list (gst_registry_get_default());
203
204     xmlstr = g_string_new ("");
205     
206     reinspect = !g_file_test ("scanobj-build.stamp", G_FILE_TEST_EXISTS);
207     
208     while (plugins) {
209       GList *features;
210       GstPlugin *plugin;
211       const gchar *source;
212       FILE *inspect = NULL;
213       gchar *inspect_name;
214
215       plugin = (GstPlugin *) (plugins->data);
216       plugins = g_list_next (plugins);
217       source = gst_plugin_get_source (plugin);
218       /*g_print ("plugin: %s source: %s\\n", plugin->desc.name, source);*/
219       if (!source || strcmp (source, "$SOURCE") != 0) {
220         continue;
221       }
222
223       /* skip static coreelements plugin with pipeline and bin element factory */
224       if (gst_plugin_get_filename (plugin) == NULL)
225         continue;
226
227       g_print ("plugin: %s source: %s\\n", plugin->desc.name, source);
228
229       if (reinspect) {
230         inspect_name = g_strdup_printf ("$INSPECT_DIR" G_DIR_SEPARATOR_S "plugin-%s.xml",
231             plugin->desc.name);
232         inspect = fopen (inspect_name, "w");
233         if (inspect == NULL) {
234           g_error ("Could not open %s for writing: %s\\n", inspect_name,
235               g_strerror (errno));
236         }
237         g_free (inspect_name);
238         
239         /* output plugin data */
240         fputs ("<plugin>\\n",inspect);
241         fputs (xmlprint(2, "name", plugin->desc.name),inspect);
242         fputs (xmlprint(2, "description", plugin->desc.description),inspect);
243         fputs (xmlprint(2, "filename", plugin->filename),inspect);
244         fputs (xmlprint(2, "basename", plugin->basename),inspect);
245         fputs (xmlprint(2, "version", plugin->desc.version),inspect);
246         fputs (xmlprint(2, "license", plugin->desc.license),inspect);
247         fputs (xmlprint(2, "source", plugin->desc.source),inspect);
248         fputs (xmlprint(2, "package", plugin->desc.package),inspect);
249         fputs (xmlprint(2, "origin", plugin->desc.origin),inspect);
250         fputs ("  <elements>\\n", inspect);
251       }
252
253       features =
254           gst_registry_get_feature_list_by_plugin (gst_registry_get_default (),
255           plugin->desc.name);
256
257       /* sort factories by feature->name */
258       features = g_list_sort (features, gst_feature_sort_compare);
259           
260       while (features) {
261         GstPluginFeature *feature;
262         feature = GST_PLUGIN_FEATURE (features->data);
263         feature = gst_plugin_feature_load (feature);
264         if (!feature) {
265           g_warning ("Could not load plugin feature %s",
266                      gst_plugin_feature_get_name (feature));
267         }
268
269         if (GST_IS_ELEMENT_FACTORY (feature)) {
270           const gchar *pad_dir[] = { "unknown","source","sink" };
271           const gchar *pad_pres[] = { "always","sometimes","request" };
272           GList *pads, *pad;
273
274           /*g_print ("  feature: %s\\n", feature->name);*/
275           
276           factory = GST_ELEMENT_FACTORY (feature);
277           factories = g_list_prepend (factories, factory);
278           
279           if (reinspect) {
280             /* output element data */
281             fputs ("    <element>\\n", inspect);
282             fputs (xmlprint(6, "name", feature->name),inspect);
283             fputs (xmlprint(6, "longname", gst_element_factory_get_longname (factory)),inspect);
284             fputs (xmlprint(6, "class", gst_element_factory_get_klass (factory)),inspect);
285             fputs (xmlprint(6, "description", gst_element_factory_get_description (factory)),inspect);
286             fputs (xmlprint(6, "author", gst_element_factory_get_author (factory)),inspect);
287             fputs ("      <pads>\\n", inspect);
288             
289             /* output pad-template data */
290             pads = g_list_copy ((GList *) gst_element_factory_get_static_pad_templates (factory));
291             pads = g_list_sort (pads, static_pad_template_compare);
292             for (pad = pads; pad != NULL; pad = pad->next) {
293               GstStaticPadTemplate *pt = pad->data;
294             
295               fputs ("        <caps>\\n", inspect);
296               fputs (xmlprint(10, "name", pt->name_template),inspect);
297               fputs (xmlprint(10, "direction", pad_dir[pt->direction]),inspect);
298               fputs (xmlprint(10, "presence", pad_pres[pt->presence]),inspect);
299               fputs (xmlprint(10, "details", pt->static_caps.string),inspect);
300               fputs ("        </caps>\\n", inspect);
301             }
302             g_list_free (pads);
303             fputs ("      </pads>\\n    </element>\\n", inspect);
304           }
305         }
306         features = g_list_next (features);
307       }
308       
309       if (reinspect) {
310         fputs ("  </elements>\\n</plugin>", inspect);
311         fclose (inspect);
312       }
313     }
314     
315     g_string_free (xmlstr, TRUE);
316
317     g_message ("number of element factories: %d", g_list_length (factories));
318
319     /* allocate the object_types array to hold them */
320     object_types = g_new0 (GType, g_list_length (factories)+$ntypes+1);
321
322     l = factories;
323     i = 0;
324
325     /* fill it */
326     while (l) {
327       factory = GST_ELEMENT_FACTORY (l->data);
328       type = gst_element_factory_get_element_type (factory);
329       if (type != 0) {
330         g_message ("adding type %p for factory %s", (void *) type, gst_element_factory_get_longname (factory));
331         object_types[i++] = type;
332       } else {
333         g_message ("type info for factory %s not found",
334             gst_element_factory_get_longname (factory));
335       }
336       l = g_list_next (l);
337     }
338
339 EOT
340
341 # get_type functions:
342 for (@types) {
343 print OUTPUT <<EOT;
344     type = $_ ();
345     if (type == 0) {
346       g_message ("$_ () didn't return a valid type");
347     }
348     else {
349       object_types[i++] = type;
350     }
351 EOT
352 }
353
354 # Implicit types retrieved from GLib:
355 for (@impl_types) {
356 print OUTPUT <<EOT;
357     type = g_type_from_name ("$_");
358     if (type == 0) {
359       g_message ("Implicit type $_ not found");
360     }
361     else {
362       object_types[i++] = type;
363     }
364 EOT
365 }
366
367 print OUTPUT <<EOT;
368
369     object_types[i] = 0;
370
371     /* Need to make sure all the types are loaded in and initialize
372      * their signals and properties.
373      */
374     for (i=0; object_types[i]; i++) {
375       if (G_TYPE_IS_CLASSED (object_types[i]))
376         g_type_class_ref (object_types[i]);
377         else {
378           g_warning ("not reffing type: %s", g_type_name (object_types[i]));
379         }
380     }
381
382     return object_types;
383 }
384
385 /*
386  * This uses GObject type functions to output signal prototypes and the object
387  * hierarchy.
388  */
389
390 /* The output files */
391 const gchar *signals_filename = "$new_signals_filename";
392 const gchar *hierarchy_filename = "$new_hierarchy_filename";
393 const gchar *interfaces_filename = "$new_interfaces_filename";
394 const gchar *prerequisites_filename = "$new_prerequisites_filename";
395 const gchar *args_filename = "$new_args_filename";
396
397
398 static void output_signals (void);
399 static void output_object_signals (FILE *fp,
400                                    GType object_type);
401 static void output_object_signal (FILE *fp,
402                                   const gchar *object_class_name,
403                                   guint signal_id);
404 static const gchar * get_type_name (GType type,
405                                     gboolean * is_pointer);
406 static const gchar * get_gdk_event (const gchar * signal_name);
407 static const gchar ** lookup_signal_arg_names (const gchar * type,
408                                                const gchar * signal_name);
409
410 static void output_object_hierarchy (void);
411 static void output_hierarchy (FILE *fp,
412                               GType type,
413                               guint level);
414
415 static void output_object_interfaces (void);
416 static void output_interfaces (FILE *fp,
417                                GType type);
418
419 static void output_interface_prerequisites (void);
420 static void output_prerequisites (FILE *fp,
421                                   GType type);
422
423 static void output_args (void);
424 static void output_object_args (FILE *fp, GType object_type);
425
426 int
427 main (int argc, char *argv[])
428 {
429   /* Silence the compiler: */
430   if (argv != argv) argc = argc;
431
432   $TYPE_INIT_FUNC;
433
434   get_object_types ();
435
436   output_signals ();
437   output_object_hierarchy ();
438   output_object_interfaces ();
439   output_interface_prerequisites ();
440   output_args ();
441
442   return 0;
443 }
444
445
446 static void
447 output_signals (void)
448 {
449   FILE *fp;
450   gint i;
451
452   fp = fopen (signals_filename, "w");
453   if (fp == NULL)
454     {
455       g_warning ("Couldn't open output file: %s : %s", signals_filename, strerror(errno));
456       return;
457     }
458
459   for (i = 0; object_types[i]; i++)
460     output_object_signals (fp, object_types[i]);
461
462   fclose (fp);
463 }
464
465 static gint
466 compare_signals (const void *a, const void *b)
467 {
468   const guint *signal_a = a;
469   const guint *signal_b = b;
470
471   return strcmp (g_signal_name (*signal_a), g_signal_name (*signal_b));
472 }
473
474 /* This outputs all the signals of one object. */
475 static void
476 output_object_signals (FILE *fp, GType object_type)
477 {
478   const gchar *object_class_name;
479   guint *signals, n_signals;
480   guint sig;
481
482   if (G_TYPE_IS_INSTANTIATABLE (object_type) ||
483       G_TYPE_IS_INTERFACE (object_type))
484     {
485
486       object_class_name = g_type_name (object_type);
487
488       signals = g_signal_list_ids (object_type, &n_signals);
489       qsort (signals, n_signals, sizeof (guint), compare_signals);
490
491       for (sig = 0; sig < n_signals; sig++)
492         {
493            output_object_signal (fp, object_class_name, signals[sig]);
494         }
495       g_free (signals);
496    }
497 }
498
499
500 /* This outputs one signal. */
501 static void
502 output_object_signal (FILE *fp,
503                       const gchar *object_name,
504                       guint signal_id)
505 {
506   GSignalQuery query_info;
507   const gchar *type_name, *ret_type, *object_arg, *arg_name;
508   gchar *pos, *object_arg_lower;
509   gboolean is_pointer;
510   gchar ret_type_buffer[1024], buffer[1024];
511   guint i, param;
512   const gchar **arg_names;
513   gint param_num, widget_num, event_num, callback_num;
514   gint *arg_num;
515   gchar signal_name[128];
516   gchar flags[16];
517
518   /*  g_print ("Object: %s Signal: %u\\n", object_name, signal_id);*/
519
520   param_num = 1;
521   widget_num = event_num = callback_num = 0;
522
523   g_signal_query (signal_id, &query_info);
524
525   /* Output the return type and function name. */
526   ret_type = get_type_name (query_info.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer);
527   sprintf (ret_type_buffer, "%s%s", ret_type, is_pointer ? "*" : "");
528
529   /* Output the signal object type and the argument name. We assume the
530      type is a pointer - I think that is OK. We remove "Gtk" or "Gnome" and
531      convert to lower case for the argument name. */
532   pos = buffer;
533   sprintf (pos, "%s ", object_name);
534   pos += strlen (pos);
535
536   if (!strncmp (object_name, "Gtk", 3))
537       object_arg = object_name + 3;
538   else if (!strncmp (object_name, "Gnome", 5))
539       object_arg = object_name + 5;
540   else
541       object_arg = object_name;
542
543   object_arg_lower = g_ascii_strdown (object_arg, -1);
544   sprintf (pos, "*%s\\n", object_arg_lower);
545   pos += strlen (pos);
546   if (!strncmp (object_arg_lower, "widget", 6))
547     widget_num = 2;
548   g_free(object_arg_lower);
549
550   /* Convert signal name to use underscores rather than dashes '-'. */
551   strcpy (signal_name, query_info.signal_name);
552   for (i = 0; signal_name[i]; i++)
553     {
554       if (signal_name[i] == '-')
555         signal_name[i] = '_';
556     }
557
558   /* Output the signal parameters. */
559   arg_names = lookup_signal_arg_names (object_name, signal_name);
560
561   for (param = 0; param < query_info.n_params; param++)
562     {
563       if (arg_names)
564         {
565           sprintf (pos, "%s\\n", arg_names[param]);
566           pos += strlen (pos);
567         }
568       else
569         {
570           type_name = get_type_name (query_info.param_types[param] & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer);
571
572           /* Most arguments to the callback are called "arg1", "arg2", etc.
573              GdkWidgets are called "widget", "widget2", ...
574              GdkEvents are called "event", "event2", ...
575              GtkCallbacks are called "callback", "callback2", ... */
576           if (!strcmp (type_name, "GtkWidget"))
577             {
578               arg_name = "widget";
579               arg_num = &widget_num;
580             }
581           else if (!strcmp (type_name, "GdkEvent"))
582             {
583               type_name = get_gdk_event (signal_name);
584               arg_name = "event";
585               arg_num = &event_num;
586               is_pointer = TRUE;
587             }
588           else if (!strcmp (type_name, "GtkCallback")
589                    || !strcmp (type_name, "GtkCCallback"))
590             {
591               arg_name = "callback";
592               arg_num = &callback_num;
593             }
594           else
595             {
596               arg_name = "arg";
597               arg_num = &param_num;
598             }
599           sprintf (pos, "%s ", type_name);
600           pos += strlen (pos);
601
602           if (!arg_num || *arg_num == 0)
603             sprintf (pos, "%s%s\\n", is_pointer ? "*" : " ", arg_name);
604           else
605             sprintf (pos, "%s%s%i\\n", is_pointer ? "*" : " ", arg_name,
606                      *arg_num);
607           pos += strlen (pos);
608
609           if (arg_num)
610             {
611               if (*arg_num == 0)
612                 *arg_num = 2;
613               else
614                 *arg_num += 1;
615             }
616         }
617     }
618
619   pos = flags;
620   /* We use one-character flags for simplicity. */
621   if (query_info.signal_flags & G_SIGNAL_RUN_FIRST)
622     *pos++ = 'f';
623   if (query_info.signal_flags & G_SIGNAL_RUN_LAST)
624     *pos++ = 'l';
625   if (query_info.signal_flags & G_SIGNAL_RUN_CLEANUP)
626     *pos++ = 'c';
627   if (query_info.signal_flags & G_SIGNAL_NO_RECURSE)
628     *pos++ = 'r';
629   if (query_info.signal_flags & G_SIGNAL_DETAILED)
630     *pos++ = 'd';
631   if (query_info.signal_flags & G_SIGNAL_ACTION)
632     *pos++ = 'a';
633   if (query_info.signal_flags & G_SIGNAL_NO_HOOKS)
634     *pos++ = 'h';
635   *pos = 0;
636
637   fprintf (fp,
638            "<SIGNAL>\\n<NAME>%s::%s</NAME>\\n<RETURNS>%s</RETURNS>\\n<FLAGS>%s</FLAGS>\\n%s</SIGNAL>\\n\\n",
639            object_name, query_info.signal_name, ret_type_buffer, flags, buffer);
640 }
641
642
643 /* Returns the type name to use for a signal argument or return value, given
644    the GtkType from the signal info. It also sets is_pointer to TRUE if the
645    argument needs a '*' since it is a pointer. */
646 static const gchar *
647 get_type_name (GType type, gboolean * is_pointer)
648 {
649   const gchar *type_name;
650
651   *is_pointer = FALSE;
652   type_name = g_type_name (type);
653
654   switch (type) {
655   case G_TYPE_NONE:
656   case G_TYPE_CHAR:
657   case G_TYPE_UCHAR:
658   case G_TYPE_BOOLEAN:
659   case G_TYPE_INT:
660   case G_TYPE_UINT:
661   case G_TYPE_LONG:
662   case G_TYPE_ULONG:
663   case G_TYPE_FLOAT:
664   case G_TYPE_DOUBLE:
665   case G_TYPE_POINTER:
666     /* These all have normal C type names so they are OK. */
667     return type_name;
668
669   case G_TYPE_STRING:
670     /* A GtkString is really a gchar*. */
671     *is_pointer = TRUE;
672     return "gchar";
673
674   case G_TYPE_ENUM:
675   case G_TYPE_FLAGS:
676     /* We use a gint for both of these. Hopefully a subtype with a decent
677        name will be registered and used instead, as GTK+ does itself. */
678     return "gint";
679
680   case G_TYPE_BOXED:
681     /* The boxed type shouldn't be used itself, only subtypes. Though we
682        return 'gpointer' just in case. */
683     return "gpointer";
684
685   case G_TYPE_PARAM:
686     /* A GParam is really a GParamSpec*. */
687     *is_pointer = TRUE;
688     return "GParamSpec";
689
690   default:
691     break;
692   }
693
694   /* For all GObject subclasses we can use the class name with a "*",
695      e.g. 'GtkWidget *'. */
696   if (g_type_is_a (type, G_TYPE_OBJECT))
697     *is_pointer = TRUE;
698
699   if (G_TYPE_IS_CLASSED (type))
700     *is_pointer = TRUE;
701
702   /* All boxed subtypes will be pointers as well. */
703   if (g_type_is_a (type, G_TYPE_BOXED))
704     *is_pointer = TRUE;
705
706   /* All pointer subtypes will be pointers as well. */
707   if (g_type_is_a (type, G_TYPE_POINTER))
708     *is_pointer = TRUE;
709
710   /* But enums are not */
711   if (g_type_is_a (type, G_TYPE_ENUM) ||
712       g_type_is_a (type, G_TYPE_FLAGS))
713     *is_pointer = FALSE;
714
715   return type_name;
716 }
717
718
719 static const gchar *
720 get_gdk_event (const gchar * signal_name)
721 {
722   static const gchar *GbGDKEvents[] =
723   {
724     "button_press_event", "GdkEventButton",
725     "button_release_event", "GdkEventButton",
726     "motion_notify_event", "GdkEventMotion",
727     "delete_event", "GdkEvent",
728     "destroy_event", "GdkEvent",
729     "expose_event", "GdkEventExpose",
730     "key_press_event", "GdkEventKey",
731     "key_release_event", "GdkEventKey",
732     "enter_notify_event", "GdkEventCrossing",
733     "leave_notify_event", "GdkEventCrossing",
734     "configure_event", "GdkEventConfigure",
735     "focus_in_event", "GdkEventFocus",
736     "focus_out_event", "GdkEventFocus",
737     "map_event", "GdkEvent",
738     "unmap_event", "GdkEvent",
739     "property_notify_event", "GdkEventProperty",
740     "selection_clear_event", "GdkEventSelection",
741     "selection_request_event", "GdkEventSelection",
742     "selection_notify_event", "GdkEventSelection",
743     "proximity_in_event", "GdkEventProximity",
744     "proximity_out_event", "GdkEventProximity",
745     "drag_begin_event", "GdkEventDragBegin",
746     "drag_request_event", "GdkEventDragRequest",
747     "drag_end_event", "GdkEventDragRequest",
748     "drop_enter_event", "GdkEventDropEnter",
749     "drop_leave_event", "GdkEventDropLeave",
750     "drop_data_available_event", "GdkEventDropDataAvailable",
751     "other_event", "GdkEventOther",
752     "client_event", "GdkEventClient",
753     "no_expose_event", "GdkEventNoExpose",
754     "visibility_notify_event", "GdkEventVisibility",
755     "window_state_event", "GdkEventWindowState",
756     "scroll_event", "GdkEventScroll",
757     NULL
758   };
759
760   gint i;
761
762   for (i = 0; GbGDKEvents[i]; i += 2)
763     {
764       if (!strcmp (signal_name, GbGDKEvents[i]))
765         return GbGDKEvents[i + 1];
766     }
767   return "GdkEvent";
768 }
769
770
771 /* This returns argument names to use for some known GTK signals.
772     It is passed a widget name, e.g. 'GtkCList' and a signal name, e.g.
773     'select_row' and it returns a pointer to an array of argument types and
774     names. */
775 static const gchar **
776 lookup_signal_arg_names (const gchar * type, const gchar * signal_name)
777 {
778   /* Each arg array starts with the object type name and the signal name,
779      and then signal arguments follow. */
780   static const gchar *GbArgTable[][16] =
781   {
782     {"GtkCList", "select_row",
783      "gint             row",
784      "gint             column",
785      "GdkEventButton  *event"},
786     {"GtkCList", "unselect_row",
787      "gint             row",
788      "gint             column",
789      "GdkEventButton  *event"},
790     {"GtkCList", "click_column",
791      "gint             column"},
792
793     {"GtkCList", "resize_column",
794      "gint             column",
795      "gint             width"},
796
797     {"GtkCList", "extend_selection",
798      "GtkScrollType    scroll_type",
799      "gfloat           position",
800      "gboolean         auto_start_selection"},
801     {"GtkCList", "scroll_vertical",
802      "GtkScrollType    scroll_type",
803      "gfloat           position"},
804     {"GtkCList", "scroll_horizontal",
805      "GtkScrollType    scroll_type",
806      "gfloat           position"},
807
808     {"GtkCTree", "tree_select_row",
809      "GtkCTreeNode    *node",
810      "gint             column"},
811     {"GtkCTree", "tree_unselect_row",
812      "GtkCTreeNode    *node",
813      "gint             column"},
814     {"GtkCTree", "tree_expand",
815      "GtkCTreeNode    *node"},
816     {"GtkCTree", "tree_collapse",
817      "GtkCTreeNode    *node"},
818     {"GtkCTree", "tree_move",
819      "GtkCTreeNode    *node",
820      "GtkCTreeNode    *new_parent",
821      "GtkCTreeNode    *new_sibling"},
822     {"GtkCTree", "change_focus_row_expansion",
823      "GtkCTreeExpansionType expansion"},
824
825     {"GtkEditable", "insert_text",
826      "gchar           *new_text",
827      "gint             new_text_length",
828      "gint            *position"},
829     {"GtkEditable", "delete_text",
830      "gint             start_pos",
831      "gint             end_pos"},
832     {"GtkEditable", "set_editable",
833      "gboolean         is_editable"},
834     {"GtkEditable", "move_cursor",
835      "gint             x",
836      "gint             y"},
837     {"GtkEditable", "move_word",
838      "gint             num_words"},
839     {"GtkEditable", "move_page",
840      "gint             x",
841      "gint             y"},
842     {"GtkEditable", "move_to_row",
843      "gint             row"},
844     {"GtkEditable", "move_to_column",
845      "gint             column"},
846
847     {"GtkEditable", "kill_char",
848      "gint             direction"},
849     {"GtkEditable", "kill_word",
850      "gint             direction"},
851     {"GtkEditable", "kill_line",
852      "gint             direction"},
853
854
855     {"GtkInputDialog", "enable_device",
856      "GdkDevice       *deviceid"},
857     {"GtkInputDialog", "disable_device",
858      "GdkDevice       *deviceid"},
859
860     {"GtkListItem", "extend_selection",
861      "GtkScrollType    scroll_type",
862      "gfloat           position",
863      "gboolean         auto_start_selection"},
864     {"GtkListItem", "scroll_vertical",
865      "GtkScrollType    scroll_type",
866      "gfloat           position"},
867     {"GtkListItem", "scroll_horizontal",
868      "GtkScrollType    scroll_type",
869      "gfloat           position"},
870
871     {"GtkMenuShell", "move_current",
872      "GtkMenuDirectionType direction"},
873     {"GtkMenuShell", "activate_current",
874      "gboolean         force_hide"},
875
876
877     {"GtkNotebook", "switch_page",
878      "GtkNotebookPage *page",
879      "guint            page_num"},
880     {"GtkStatusbar", "text_pushed",
881      "guint            context_id",
882      "gchar           *text"},
883     {"GtkStatusbar", "text_popped",
884      "guint            context_id",
885      "gchar           *text"},
886     {"GtkTipsQuery", "widget_entered",
887      "GtkWidget       *widget",
888      "gchar           *tip_text",
889      "gchar           *tip_private"},
890     {"GtkTipsQuery", "widget_selected",
891      "GtkWidget       *widget",
892      "gchar           *tip_text",
893      "gchar           *tip_private",
894      "GdkEventButton  *event"},
895     {"GtkToolbar", "orientation_changed",
896      "GtkOrientation   orientation"},
897     {"GtkToolbar", "style_changed",
898      "GtkToolbarStyle  style"},
899     {"GtkWidget", "draw",
900      "GdkRectangle    *area"},
901     {"GtkWidget", "size_request",
902      "GtkRequisition  *requisition"},
903     {"GtkWidget", "size_allocate",
904      "GtkAllocation   *allocation"},
905     {"GtkWidget", "state_changed",
906      "GtkStateType     state"},
907     {"GtkWidget", "style_set",
908      "GtkStyle        *previous_style"},
909
910     {"GtkWidget", "install_accelerator",
911      "gchar           *signal_name",
912      "gchar            key",
913      "gint             modifiers"},
914
915     {"GtkWidget", "add_accelerator",
916      "guint            accel_signal_id",
917      "GtkAccelGroup   *accel_group",
918      "guint            accel_key",
919      "GdkModifierType  accel_mods",
920      "GtkAccelFlags    accel_flags"},
921
922     {"GtkWidget", "parent_set",
923      "GtkObject       *old_parent"},
924
925     {"GtkWidget", "remove_accelerator",
926      "GtkAccelGroup   *accel_group",
927      "guint            accel_key",
928      "GdkModifierType  accel_mods"},
929     {"GtkWidget", "debug_msg",
930      "gchar           *message"},
931     {"GtkWindow", "move_resize",
932      "gint            *x",
933      "gint            *y",
934      "gint             width",
935      "gint             height"},
936     {"GtkWindow", "set_focus",
937      "GtkWidget       *widget"},
938
939     {"GtkWidget", "selection_get",
940      "GtkSelectionData *data",
941      "guint            info",
942      "guint            time"},
943     {"GtkWidget", "selection_received",
944      "GtkSelectionData *data",
945      "guint            time"},
946
947     {"GtkWidget", "drag_begin",
948      "GdkDragContext  *drag_context"},
949     {"GtkWidget", "drag_end",
950      "GdkDragContext  *drag_context"},
951     {"GtkWidget", "drag_data_delete",
952      "GdkDragContext  *drag_context"},
953     {"GtkWidget", "drag_leave",
954      "GdkDragContext  *drag_context",
955      "guint            time"},
956     {"GtkWidget", "drag_motion",
957      "GdkDragContext  *drag_context",
958      "gint             x",
959      "gint             y",
960      "guint            time"},
961     {"GtkWidget", "drag_drop",
962      "GdkDragContext  *drag_context",
963      "gint             x",
964      "gint             y",
965      "guint            time"},
966     {"GtkWidget", "drag_data_get",
967      "GdkDragContext  *drag_context",
968      "GtkSelectionData *data",
969      "guint            info",
970      "guint            time"},
971     {"GtkWidget", "drag_data_received",
972      "GdkDragContext  *drag_context",
973      "gint             x",
974      "gint             y",
975      "GtkSelectionData *data",
976      "guint            info",
977      "guint            time"},
978
979     {NULL}
980   };
981
982   gint i;
983
984   for (i = 0; GbArgTable[i][0]; i++)
985     {
986 #if 1
987       if (!strcmp (type, GbArgTable[i][0])
988           && !strcmp (signal_name, GbArgTable[i][1]))
989         return &GbArgTable[i][2];
990 #endif
991     }
992   return NULL;
993 }
994
995 /* This outputs the hierarchy of all objects which have been initialized,
996    i.e. by calling their XXX_get_type() initialization function. */
997 static void
998 output_object_hierarchy (void)
999 {
1000   FILE *fp;
1001   gint i;
1002
1003   fp = fopen (hierarchy_filename, "w");
1004   if (fp == NULL)
1005     {
1006       g_warning ("Couldn't open output file: %s : %s", hierarchy_filename, strerror(errno));
1007       return;
1008     }
1009   output_hierarchy (fp, G_TYPE_OBJECT, 0);
1010   output_hierarchy (fp, G_TYPE_INTERFACE, 0);
1011
1012   for (i=0; object_types[i]; i++) {
1013     if (!g_type_parent (object_types[i]) &&
1014       (object_types[i] != G_TYPE_NONE) &&
1015       (object_types[i] != G_TYPE_OBJECT) &&
1016       (object_types[i] != G_TYPE_INTERFACE)
1017     ) {
1018       g_warning ("printing hierarchy for root type: %s",
1019           g_type_name (object_types[i]));
1020       output_hierarchy (fp, object_types[i], 0);
1021     }
1022   }
1023   /* for debugging
1024   for (i=0; object_types[i]; i++) {
1025     if(object_types[i] != G_TYPE_NONE) {
1026       g_print ("type has not been added to hierarchy: %s\\n",
1027         g_type_name (object_types[i]));
1028     }
1029   }
1030   for debugging */
1031
1032   fclose (fp);
1033 }
1034
1035 static int
1036 type_cmp (const void * p1, const void * p2)
1037 {
1038   return strcmp (g_type_name (*((GType *) p1)), g_type_name (*((GType *) p2)));
1039 }
1040
1041 /* This is called recursively to output the hierarchy of a widget. */
1042 static void
1043 output_hierarchy (FILE  *fp,
1044                   GType  type,
1045                   guint   level)
1046 {
1047   guint i;
1048   GType *children;
1049   guint n_children;
1050
1051   if (!type)
1052     return;
1053
1054   /* for debugging
1055   for (i=0; object_types[i]; i++) {
1056     if(object_types[i] == type) {
1057       g_print ("added type to hierarchy (level %d): %s\\n",
1058         level, g_type_name (type));
1059       object_types[i] = G_TYPE_NONE;
1060       break;
1061     }
1062   }
1063   for debugging */
1064
1065   for (i = 0; i < level; i++)
1066     fprintf (fp, "  ");
1067   fprintf (fp, "%s", g_type_name (type));
1068   fprintf (fp, "\\n");
1069
1070   children = g_type_children (type, &n_children);
1071
1072   qsort (&children[0], n_children, sizeof (GType), type_cmp);
1073
1074   for (i=0; i < n_children; i++) {
1075     output_hierarchy (fp, children[i], level + 1);
1076   }
1077
1078   g_free (children);
1079 }
1080
1081 static void output_object_interfaces (void)
1082 {
1083   guint i;
1084   FILE *fp;
1085
1086   fp = fopen (interfaces_filename, "w");
1087   if (fp == NULL)
1088     {
1089       g_warning ("Couldn't open output file: %s : %s", interfaces_filename, strerror(errno));
1090       return;
1091     }
1092   output_interfaces (fp, G_TYPE_OBJECT);
1093
1094   for (i = 0; object_types[i]; i++)
1095     {
1096       if (!g_type_parent (object_types[i]) &&
1097           (object_types[i] != G_TYPE_OBJECT) &&
1098           G_TYPE_IS_INSTANTIATABLE (object_types[i]))
1099         {
1100           output_interfaces (fp, object_types[i]);
1101         }
1102     }
1103   fclose (fp);
1104 }
1105
1106 static void
1107 output_interfaces (FILE  *fp,
1108                    GType  type)
1109 {
1110   guint i;
1111   GType *children, *interfaces;
1112   guint n_children, n_interfaces;
1113
1114   if (!type)
1115     return;
1116
1117   interfaces = g_type_interfaces (type, &n_interfaces);
1118
1119   if (n_interfaces > 0)
1120     {
1121       fprintf (fp, "%s", g_type_name (type));
1122       for (i=0; i < n_interfaces; i++)
1123           fprintf (fp, " %s", g_type_name (interfaces[i]));
1124       fprintf (fp, "\\n");
1125      }
1126   g_free (interfaces);
1127
1128   children = g_type_children (type, &n_children);
1129
1130   for (i=0; i < n_children; i++)
1131     output_interfaces (fp, children[i]);
1132
1133   g_free (children);
1134 }
1135
1136 static void output_interface_prerequisites (void)
1137 {
1138   FILE *fp;
1139
1140   fp = fopen (prerequisites_filename, "w");
1141   if (fp == NULL)
1142     {
1143       g_warning ("Couldn't open output file: %s : %s", prerequisites_filename, strerror(errno));
1144       return;
1145     }
1146   output_prerequisites (fp, G_TYPE_INTERFACE);
1147   fclose (fp);
1148 }
1149
1150 static void
1151 output_prerequisites (FILE  *fp,
1152                       GType  type)
1153 {
1154 #if GLIB_CHECK_VERSION(2,1,0)
1155   guint i;
1156   GType *children, *prerequisites;
1157   guint n_children, n_prerequisites;
1158
1159   if (!type)
1160     return;
1161
1162   prerequisites = g_type_interface_prerequisites (type, &n_prerequisites);
1163
1164   if (n_prerequisites > 0)
1165     {
1166       fprintf (fp, "%s", g_type_name (type));
1167       for (i=0; i < n_prerequisites; i++)
1168           fprintf (fp, " %s", g_type_name (prerequisites[i]));
1169       fprintf (fp, "\\n");
1170      }
1171   g_free (prerequisites);
1172
1173   children = g_type_children (type, &n_children);
1174
1175   for (i=0; i < n_children; i++)
1176     output_prerequisites (fp, children[i]);
1177
1178   g_free (children);
1179 #endif
1180 }
1181
1182 static void
1183 output_args (void)
1184 {
1185   FILE *fp;
1186   gint i;
1187
1188   fp = fopen (args_filename, "w");
1189   if (fp == NULL)
1190     {
1191       g_warning ("Couldn't open output file: %s : %s", args_filename, strerror(errno));
1192       return;
1193     }
1194
1195   for (i = 0; object_types[i]; i++) {
1196     output_object_args (fp, object_types[i]);
1197   }
1198
1199   fclose (fp);
1200 }
1201
1202 static gint
1203 compare_param_specs (const void *a, const void *b)
1204 {
1205   GParamSpec *spec_a = *(GParamSpec **)a;
1206   GParamSpec *spec_b = *(GParamSpec **)b;
1207
1208   return strcmp (g_param_spec_get_name (spec_a), g_param_spec_get_name (spec_b));
1209 }
1210
1211 /* Its common to have unsigned properties restricted
1212  * to the signed range. Therefore we make this look
1213  * a bit nicer by spelling out the max constants.
1214  */
1215
1216 /* Don't use "==" with floats, it might trigger a gcc warning.  */
1217 #define GTKDOC_COMPARE_FLOAT(x, y) (x <= y && x >= y)
1218
1219 static gchar*
1220 describe_double_constant (gdouble value)
1221 {
1222   gchar *desc;
1223
1224   if (GTKDOC_COMPARE_FLOAT (value, G_MAXDOUBLE))
1225     desc = g_strdup ("G_MAXDOUBLE");
1226   else if (GTKDOC_COMPARE_FLOAT (value, G_MINDOUBLE))
1227     desc = g_strdup ("G_MINDOUBLE");
1228   else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXDOUBLE))
1229     desc = g_strdup ("-G_MAXDOUBLE");
1230   else if (GTKDOC_COMPARE_FLOAT (value, G_MAXFLOAT))
1231     desc = g_strdup ("G_MAXFLOAT");
1232   else if (GTKDOC_COMPARE_FLOAT (value, G_MINFLOAT))
1233     desc = g_strdup ("G_MINFLOAT");
1234   else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXFLOAT))
1235     desc = g_strdup ("-G_MAXFLOAT");
1236   else {
1237     /* make sure floats are output with a decimal dot irrespective of
1238      * current locale. Use formatd since we want human-readable numbers
1239      * and do not need the exact same bit representation when deserialising */
1240     desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE);
1241     g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g", value);
1242   }
1243
1244   return desc;
1245 }
1246
1247 static gchar*
1248 describe_signed_constant (gint64 value)
1249 {
1250   gchar *desc;
1251
1252   if (value == G_MAXINT)
1253     desc = g_strdup ("G_MAXINT");
1254   else if (value == G_MININT)
1255     desc = g_strdup ("G_MININT");
1256   else if (value == G_MAXUINT)
1257     desc = g_strdup ("G_MAXUINT");
1258   else if (value == G_MAXLONG)
1259     desc = g_strdup ("G_MAXLONG");
1260   else if (value == G_MINLONG)
1261     desc = g_strdup ("G_MINLONG");
1262   else if (value == G_MAXULONG)
1263     desc = g_strdup ("G_MAXULONG");
1264   else if (value == G_MAXINT64)
1265     desc = g_strdup ("G_MAXINT64");
1266   else if (value == G_MININT64)
1267     desc = g_strdup ("G_MININT64");
1268   else
1269     desc = g_strdup_printf ("%" G_GINT64_FORMAT, value);
1270
1271   return desc;
1272 }
1273
1274 static gchar*
1275 describe_unsigned_constant (guint64 value)
1276 {
1277   gchar *desc;
1278
1279   if (value == G_MAXINT)
1280     desc = g_strdup ("G_MAXINT");
1281   else if (value == G_MININT)
1282     desc = g_strdup ("G_MININT");
1283   else if (value == G_MAXUINT)
1284     desc = g_strdup ("G_MAXUINT");
1285   else if (value == G_MAXLONG)
1286     desc = g_strdup ("G_MAXLONG");
1287   else if (value == G_MINLONG)
1288     desc = g_strdup ("G_MINLONG");
1289   else if (value == G_MAXULONG)
1290     desc = g_strdup ("G_MAXULONG");
1291   else if (value == G_MAXINT64)
1292     desc = g_strdup ("G_MAXINT64");
1293   else if (value == G_MININT64)
1294     desc = g_strdup ("G_MININT64");
1295   else if (value == G_MAXUINT64)
1296     desc = g_strdup ("G_MAXUINT64");
1297   else
1298     desc = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1299
1300   return desc;
1301 }
1302
1303 static gchar*
1304 describe_type (GParamSpec *spec)
1305 {
1306   gchar *desc;
1307   gchar *lower;
1308   gchar *upper;
1309
1310   if (G_IS_PARAM_SPEC_CHAR (spec))
1311     {
1312       GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec);
1313
1314       lower = describe_signed_constant (pspec->minimum);
1315       upper = describe_signed_constant (pspec->maximum);
1316       if (pspec->minimum == G_MININT8 && pspec->maximum == G_MAXINT8)
1317         desc = g_strdup ("");
1318       else if (pspec->minimum == G_MININT8)
1319         desc = g_strdup_printf ("<= %s", upper);
1320       else if (pspec->maximum == G_MAXINT8)
1321         desc = g_strdup_printf (">= %s", lower);
1322       else
1323         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1324       g_free (lower);
1325       g_free (upper);
1326     }
1327   else if (G_IS_PARAM_SPEC_UCHAR (spec))
1328     {
1329       GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec);
1330
1331       lower = describe_unsigned_constant (pspec->minimum);
1332       upper = describe_unsigned_constant (pspec->maximum);
1333       if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT8)
1334         desc = g_strdup ("");
1335       else if (pspec->minimum == 0)
1336         desc = g_strdup_printf ("<= %s", upper);
1337       else if (pspec->maximum == G_MAXUINT8)
1338         desc = g_strdup_printf (">= %s", lower);
1339       else
1340         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1341       g_free (lower);
1342       g_free (upper);
1343     }
1344   else if (G_IS_PARAM_SPEC_INT (spec))
1345     {
1346       GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec);
1347
1348       lower = describe_signed_constant (pspec->minimum);
1349       upper = describe_signed_constant (pspec->maximum);
1350       if (pspec->minimum == G_MININT && pspec->maximum == G_MAXINT)
1351         desc = g_strdup ("");
1352       else if (pspec->minimum == G_MININT)
1353         desc = g_strdup_printf ("<= %s", upper);
1354       else if (pspec->maximum == G_MAXINT)
1355         desc = g_strdup_printf (">= %s", lower);
1356       else
1357         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1358       g_free (lower);
1359       g_free (upper);
1360     }
1361   else if (G_IS_PARAM_SPEC_UINT (spec))
1362     {
1363       GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec);
1364
1365       lower = describe_unsigned_constant (pspec->minimum);
1366       upper = describe_unsigned_constant (pspec->maximum);
1367       if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT)
1368         desc = g_strdup ("");
1369       else if (pspec->minimum == 0)
1370         desc = g_strdup_printf ("<= %s", upper);
1371       else if (pspec->maximum == G_MAXUINT)
1372         desc = g_strdup_printf (">= %s", lower);
1373       else
1374         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1375       g_free (lower);
1376       g_free (upper);
1377     }
1378   else if (G_IS_PARAM_SPEC_LONG (spec))
1379     {
1380       GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec);
1381
1382       lower = describe_signed_constant (pspec->minimum);
1383       upper = describe_signed_constant (pspec->maximum);
1384       if (pspec->minimum == G_MINLONG && pspec->maximum == G_MAXLONG)
1385         desc = g_strdup ("");
1386       else if (pspec->minimum == G_MINLONG)
1387         desc = g_strdup_printf ("<= %s", upper);
1388       else if (pspec->maximum == G_MAXLONG)
1389         desc = g_strdup_printf (">= %s", lower);
1390       else
1391         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1392       g_free (lower);
1393       g_free (upper);
1394     }
1395   else if (G_IS_PARAM_SPEC_ULONG (spec))
1396     {
1397       GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec);
1398       gchar *upper;
1399
1400       lower = describe_unsigned_constant (pspec->minimum);
1401       upper = describe_unsigned_constant (pspec->maximum);
1402       if (pspec->minimum == 0 && pspec->maximum == G_MAXULONG)
1403         desc = g_strdup ("");
1404       else if (pspec->minimum == 0)
1405         desc = g_strdup_printf ("<= %s", upper);
1406       else if (pspec->maximum == G_MAXULONG)
1407         desc = g_strdup_printf (">= %s", lower);
1408       else
1409         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1410       g_free (lower);
1411       g_free (upper);
1412     }
1413   else if (G_IS_PARAM_SPEC_INT64 (spec))
1414     {
1415       GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec);
1416
1417       lower = describe_signed_constant (pspec->minimum);
1418       upper = describe_signed_constant (pspec->maximum);
1419       if (pspec->minimum == G_MININT64 && pspec->maximum == G_MAXINT64)
1420         desc = g_strdup ("");
1421       else if (pspec->minimum == G_MININT64)
1422         desc = g_strdup_printf ("<= %s", upper);
1423       else if (pspec->maximum == G_MAXINT64)
1424         desc = g_strdup_printf (">= %s", lower);
1425       else
1426         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1427       g_free (lower);
1428       g_free (upper);
1429     }
1430   else if (G_IS_PARAM_SPEC_UINT64 (spec))
1431     {
1432       GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec);
1433
1434       lower = describe_unsigned_constant (pspec->minimum);
1435       upper = describe_unsigned_constant (pspec->maximum);
1436       if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT64)
1437         desc = g_strdup ("");
1438       else if (pspec->minimum == 0)
1439         desc = g_strdup_printf ("<= %s", upper);
1440       else if (pspec->maximum == G_MAXUINT64)
1441         desc = g_strdup_printf (">= %s", lower);
1442       else
1443         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1444       g_free (lower);
1445       g_free (upper);
1446     }
1447   else if (G_IS_PARAM_SPEC_FLOAT (spec))
1448     {
1449       GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec);
1450
1451       lower = describe_double_constant (pspec->minimum);
1452       upper = describe_double_constant (pspec->maximum);
1453       if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXFLOAT))
1454         {
1455           if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT))
1456             desc = g_strdup ("");
1457           else
1458             desc = g_strdup_printf ("<= %s", upper);
1459         }
1460       else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT))
1461         desc = g_strdup_printf (">= %s", lower);
1462       else
1463         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1464       g_free (lower);
1465       g_free (upper);
1466     }
1467   else if (G_IS_PARAM_SPEC_DOUBLE (spec))
1468     {
1469       GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec);
1470
1471       lower = describe_double_constant (pspec->minimum);
1472       upper = describe_double_constant (pspec->maximum);
1473       if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXDOUBLE))
1474         {
1475           if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE))
1476             desc = g_strdup ("");
1477           else
1478             desc = g_strdup_printf ("<= %s", upper);
1479         }
1480       else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE))
1481         desc = g_strdup_printf (">= %s", lower);
1482       else
1483         desc = g_strdup_printf ("[%s,%s]", lower, upper);
1484       g_free (lower);
1485       g_free (upper);
1486     }
1487   else
1488     {
1489       desc = g_strdup ("");
1490     }
1491
1492   return desc;
1493 }
1494
1495 static gchar*
1496 describe_default (GParamSpec *spec)
1497 {
1498   gchar *desc;
1499
1500   if (G_IS_PARAM_SPEC_CHAR (spec))
1501     {
1502       GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec);
1503
1504       desc = g_strdup_printf ("%d", pspec->default_value);
1505     }
1506   else if (G_IS_PARAM_SPEC_UCHAR (spec))
1507     {
1508       GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec);
1509
1510       desc = g_strdup_printf ("%u", pspec->default_value);
1511     }
1512   else if (G_IS_PARAM_SPEC_BOOLEAN (spec))
1513     {
1514       GParamSpecBoolean *pspec = G_PARAM_SPEC_BOOLEAN (spec);
1515
1516       desc = g_strdup_printf ("%s", pspec->default_value ? "TRUE" : "FALSE");
1517     }
1518   else if (G_IS_PARAM_SPEC_INT (spec))
1519     {
1520       GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec);
1521
1522       desc = g_strdup_printf ("%d", pspec->default_value);
1523     }
1524   else if (G_IS_PARAM_SPEC_UINT (spec))
1525     {
1526       GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec);
1527
1528       desc = g_strdup_printf ("%u", pspec->default_value);
1529     }
1530   else if (G_IS_PARAM_SPEC_LONG (spec))
1531     {
1532       GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec);
1533
1534       desc = g_strdup_printf ("%ld", pspec->default_value);
1535     }
1536   else if (G_IS_PARAM_SPEC_LONG (spec))
1537     {
1538       GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec);
1539
1540       desc = g_strdup_printf ("%lu", pspec->default_value);
1541     }
1542   else if (G_IS_PARAM_SPEC_INT64 (spec))
1543     {
1544       GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec);
1545
1546       desc = g_strdup_printf ("%" G_GINT64_FORMAT, pspec->default_value);
1547     }
1548   else if (G_IS_PARAM_SPEC_UINT64 (spec))
1549     {
1550       GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec);
1551
1552       desc = g_strdup_printf ("%" G_GUINT64_FORMAT, pspec->default_value);
1553     }
1554   else if (G_IS_PARAM_SPEC_UNICHAR (spec))
1555     {
1556       GParamSpecUnichar *pspec = G_PARAM_SPEC_UNICHAR (spec);
1557
1558       if (g_unichar_isprint (pspec->default_value))
1559         desc = g_strdup_printf ("'%c'", pspec->default_value);
1560       else
1561         desc = g_strdup_printf ("%u", pspec->default_value);
1562     }
1563   else if (G_IS_PARAM_SPEC_ENUM (spec))
1564     {
1565       GParamSpecEnum *pspec = G_PARAM_SPEC_ENUM (spec);
1566
1567       GEnumValue *value = g_enum_get_value (pspec->enum_class, pspec->default_value);
1568       if (value)
1569         desc = g_strdup_printf ("%s", value->value_name);
1570       else
1571         desc = g_strdup_printf ("%d", pspec->default_value);
1572     }
1573   else if (G_IS_PARAM_SPEC_FLAGS (spec))
1574     {
1575       GParamSpecFlags *pspec = G_PARAM_SPEC_FLAGS (spec);
1576       guint default_value;
1577       GString *acc;
1578
1579       default_value = pspec->default_value;
1580       acc = g_string_new ("");
1581
1582       while (default_value)
1583         {
1584           GFlagsValue *value = g_flags_get_first_value (pspec->flags_class, default_value);
1585
1586           if (!value)
1587             break;
1588
1589           if (acc->len > 0)
1590             g_string_append (acc, "|");
1591           g_string_append (acc, value->value_name);
1592
1593           default_value &= ~value->value;
1594         }
1595
1596       if (default_value == 0)
1597         desc = g_string_free (acc, FALSE);
1598       else
1599         {
1600           desc = g_strdup_printf ("%d", pspec->default_value);
1601           g_string_free (acc, TRUE);
1602         }
1603     }
1604   else if (G_IS_PARAM_SPEC_FLOAT (spec))
1605     {
1606       GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec);
1607
1608       /* make sure floats are output with a decimal dot irrespective of
1609        * current locale. Use formatd since we want human-readable numbers
1610        * and do not need the exact same bit representation when deserialising */
1611       desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE);
1612       g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g",
1613           pspec->default_value);
1614     }
1615   else if (G_IS_PARAM_SPEC_DOUBLE (spec))
1616     {
1617       GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec);
1618
1619       /* make sure floats are output with a decimal dot irrespective of
1620        * current locale. Use formatd since we want human-readable numbers
1621        * and do not need the exact same bit representation when deserialising */
1622       desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE);
1623       g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g",
1624           pspec->default_value);
1625     }
1626   else if (G_IS_PARAM_SPEC_STRING (spec))
1627     {
1628       GParamSpecString *pspec = G_PARAM_SPEC_STRING (spec);
1629
1630       if (pspec->default_value)
1631         {
1632           gchar *esc = g_strescape (pspec->default_value, NULL);
1633
1634           desc = g_strdup_printf ("\\"%s\\"", esc);
1635
1636           g_free (esc);
1637         }
1638       else
1639         desc = g_strdup_printf ("NULL");
1640     }
1641   else
1642     {
1643       desc = g_strdup ("");
1644     }
1645
1646   return desc;
1647 }
1648
1649
1650 static void
1651 output_object_args (FILE *fp, GType object_type)
1652 {
1653   gpointer class;
1654   const gchar *object_class_name;
1655   guint arg;
1656   gchar flags[16], *pos;
1657   GParamSpec **properties;
1658   guint n_properties;
1659   gboolean child_prop;
1660   gboolean style_prop;
1661   gboolean is_pointer;
1662   const gchar *type_name;
1663   gchar *type_desc;
1664   gchar *default_value;
1665
1666   if (G_TYPE_IS_OBJECT (object_type))
1667     {
1668       class = g_type_class_peek (object_type);
1669       if (!class)
1670         return;
1671
1672       properties = g_object_class_list_properties (class, &n_properties);
1673     }
1674 #if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 3)
1675   else if (G_TYPE_IS_INTERFACE (object_type))
1676     {
1677       class = g_type_default_interface_ref (object_type);
1678
1679       if (!class)
1680         return;
1681
1682       properties = g_object_interface_list_properties (class, &n_properties);
1683     }
1684 #endif
1685   else
1686     return;
1687
1688   object_class_name = g_type_name (object_type);
1689
1690   child_prop = FALSE;
1691   style_prop = FALSE;
1692
1693   while (TRUE) {
1694     qsort (properties, n_properties, sizeof (GParamSpec *), compare_param_specs);
1695     for (arg = 0; arg < n_properties; arg++)
1696       {
1697         GParamSpec *spec = properties[arg];
1698         const gchar *nick, *blurb, *dot;
1699
1700         if (spec->owner_type != object_type)
1701           continue;
1702
1703         pos = flags;
1704         /* We use one-character flags for simplicity. */
1705         if (child_prop && !style_prop)
1706           *pos++ = 'c';
1707         if (style_prop)
1708           *pos++ = 's';
1709         if (spec->flags & G_PARAM_READABLE)
1710           *pos++ = 'r';
1711         if (spec->flags & G_PARAM_WRITABLE)
1712           *pos++ = 'w';
1713         if (spec->flags & G_PARAM_CONSTRUCT)
1714           *pos++ = 'x';
1715         if (spec->flags & G_PARAM_CONSTRUCT_ONLY)
1716           *pos++ = 'X';
1717         *pos = 0;
1718
1719         nick = g_param_spec_get_nick (spec);
1720         blurb = g_param_spec_get_blurb (spec);
1721
1722         dot = "";
1723         if (blurb) {
1724           int str_len = strlen (blurb);
1725           if (str_len > 0  && blurb[str_len - 1] != '.')
1726             dot = ".";
1727         }
1728
1729         type_desc = describe_type (spec);
1730         default_value = describe_default (spec);
1731         type_name = get_type_name (spec->value_type, &is_pointer);
1732         fprintf (fp, "<ARG>\\n<NAME>%s::%s</NAME>\\n<TYPE>%s%s</TYPE>\\n<RANGE>%s</RANGE>\\n<FLAGS>%s</FLAGS>\\n<NICK>%s</NICK>\\n<BLURB>%s%s</BLURB>\\n<DEFAULT>%s</DEFAULT>\\n</ARG>\\n\\n",
1733                  object_class_name, g_param_spec_get_name (spec), type_name, is_pointer ? "*" : "", type_desc, flags, nick ? nick : "(null)", blurb ? blurb : "(null)", dot, default_value);
1734         g_free (type_desc);
1735         g_free (default_value);
1736       }
1737
1738     g_free (properties);
1739
1740 #ifdef GTK_IS_CONTAINER_CLASS
1741     if (!child_prop && GTK_IS_CONTAINER_CLASS (class)) {
1742       properties = gtk_container_class_list_child_properties (class, &n_properties);
1743       child_prop = TRUE;
1744       continue;
1745     }
1746 #endif
1747
1748 #ifdef GTK_IS_WIDGET_CLASS
1749 #if GTK_CHECK_VERSION(2,1,0)
1750     if (!style_prop && GTK_IS_WIDGET_CLASS (class)) {
1751       properties = gtk_widget_class_list_style_properties (GTK_WIDGET_CLASS (class), &n_properties);
1752       style_prop = TRUE;
1753       continue;
1754     }
1755 #endif
1756 #endif
1757
1758     break;
1759   }
1760 }
1761 EOT
1762
1763 close OUTPUT;
1764
1765 # Compile and run our file
1766
1767 $CC = $ENV{CC} ? $ENV{CC} : "gcc";
1768 $LD = $ENV{LD} ? $ENV{LD} : $CC;
1769 $CFLAGS = $ENV{CFLAGS} ? "$ENV{CFLAGS}" : "";
1770 $LDFLAGS = $ENV{LDFLAGS} ? $ENV{LDFLAGS} : "";
1771
1772 my $o_file;
1773 if ($CC =~ /libtool/) {
1774   $o_file  = "$MODULE-scan.lo"
1775 } else {
1776   $o_file = "$MODULE-scan.o"
1777 }
1778
1779 print "gtk-doc: Compiling scanner\n";
1780 $command = "$CC $CFLAGS -c -o $o_file $MODULE-scan.c";
1781 system($command) == 0 or die "Compilation of scanner failed: $!\n";
1782
1783 print "gtk-doc: Linking scanner\n";
1784 $command = "$LD -o $MODULE-scan $o_file $LDFLAGS";
1785 system($command) == 0 or die "Linking of scanner failed: $!\n";
1786
1787 print "gtk-doc: Running scanner $MODULE-scan\n";
1788 system("sh -c ./$MODULE-scan") == 0 or die "Scan failed: $!\n";
1789
1790 if (!defined($ENV{"GTK_DOC_KEEP_INTERMEDIATE"})) {
1791   unlink "./$MODULE-scan.c", "./$MODULE-scan.o", "./$MODULE-scan.lo", "./$MODULE-scan";
1792 }
1793
1794 #&UpdateFileIfChanged ($old_signals_filename, $new_signals_filename, 0);
1795 &UpdateFileIfChanged ($old_hierarchy_filename, $new_hierarchy_filename, 0);
1796 &UpdateFileIfChanged ($old_interfaces_filename, $new_interfaces_filename, 0);
1797 &UpdateFileIfChanged ($old_prerequisites_filename, $new_prerequisites_filename, 0);
1798 #&UpdateFileIfChanged ($old_args_filename, $new_args_filename, 0);
1799
1800