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