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