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