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