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