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