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