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