[farsight2/master] Add gst plugin documentation scripts imported from the gst cvs

Olivier Crête olivier.crete at collabora.co.uk
Tue Dec 23 15:25:29 PST 2008


---
 common/gst-xmlinspect.py                    |  168 +++
 common/gstdoc-scangobj                      | 1644 +++++++++++++++++++++++++++
 common/gtk-doc-plugins.mak                  |  384 +++++++
 common/mangle-tmpl.py                       |  158 +++
 common/plugins.xsl                          |  209 ++++
 common/scangobj-merge.py                    |  278 +++++
 configure.ac                                |    1 +
 docs/Makefile.am                            |    4 +-
 docs/plugins/Makefile.am                    |   98 ++
 docs/plugins/farsight2-plugins-docs.sgml    |   20 +
 docs/plugins/farsight2-plugins-sections.txt |   47 +
 docs/plugins/farsight2-plugins.types        |    1 +
 12 files changed, 3009 insertions(+), 3 deletions(-)
 create mode 100644 common/gst-xmlinspect.py
 create mode 100755 common/gstdoc-scangobj
 create mode 100644 common/gtk-doc-plugins.mak
 create mode 100644 common/mangle-tmpl.py
 create mode 100644 common/plugins.xsl
 create mode 100755 common/scangobj-merge.py
 create mode 100644 docs/plugins/Makefile.am
 create mode 100644 docs/plugins/farsight2-plugins-docs.sgml
 create mode 100644 docs/plugins/farsight2-plugins-sections.txt
 create mode 100644 docs/plugins/farsight2-plugins.types

diff --git a/common/gst-xmlinspect.py b/common/gst-xmlinspect.py
new file mode 100644
index 0000000..0d7f696
--- /dev/null
+++ b/common/gst-xmlinspect.py
@@ -0,0 +1,168 @@
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+"""
+examine all plugins and elements and output xml documentation for them
+used as part of the plugin documentation build
+"""
+
+import sys
+import os
+import pygst
+pygst.require('0.10')
+import gst
+
+INDENT_SIZE = 2
+
+# all templates
+
+PAD_TEMPLATE = """<caps>
+  <name>%(name)s</name>
+  <direction>%(direction)s</direction>
+  <presence>%(presence)s</presence>
+  <details>%(details)s</details>
+</caps>"""
+
+ELEMENT_TEMPLATE = """<element>
+  <name>%(name)s</name>
+  <longname>%(longname)s</longname>
+  <class>%(class)s</class>
+  <description>%(description)s</description>
+  <author>%(author)s</author>
+  <pads>
+%(pads)s
+  </pads>
+</element>"""
+
+PLUGIN_TEMPLATE = """<plugin>
+  <name>%(name)s</name>
+  <description>%(description)s</description>
+  <filename>%(filename)s</filename>
+  <basename>%(basename)s</basename>
+  <version>%(version)s</version>
+  <license>%(license)s</license>
+  <source>%(source)s</source>
+  <package>%(package)s</package>
+  <origin>%(origin)s</origin>
+  <elements>
+%(elements)s
+  </elements>
+</plugin>"""
+
+def xmlencode(line):
+    """
+    Replace &, <, and >
+    """
+    line = "&amp;".join(line.split("&"))
+    line = "&lt;".join(line.split("<"))
+    line = "&gt;".join(line.split(">"))
+    return line
+
+def get_offset(indent):
+    return " " * INDENT_SIZE * indent
+
+def output_pad_template(pt, indent=0):
+    print  "PAD TEMPLATE", pt.name_template
+    paddir = ("unknown","source","sink")
+    padpres = ("always","sometimes","request")
+    
+    d = {
+      'name':        xmlencode(pt.name_template),
+      'direction':   xmlencode(paddir[pt.direction]),
+      'presence':    xmlencode(padpres[pt.presence]),
+      'details':     xmlencode(pt.static_caps.string),
+    }
+    block = PAD_TEMPLATE % d
+
+    offset = get_offset(indent)
+    return offset + ("\n" + offset).join(block.split("\n"))
+    
+def output_element_factory(elf, indent=0):
+    print  "ELEMENT", elf.get_name()
+
+    padsoutput = []
+    padtemplates = elf.get_static_pad_templates()
+    for padtemplate in padtemplates:
+        padsoutput.append(output_pad_template(padtemplate, indent))
+
+    d = {
+        'name':        xmlencode(elf.get_name()),
+        'longname':    xmlencode(elf.get_longname()),
+        'class':       xmlencode(elf.get_klass()),
+        'description': xmlencode(elf.get_description()),
+        'author':      xmlencode(elf.get_author()),
+        'pads': "\n".join(padsoutput),
+    }
+    block = ELEMENT_TEMPLATE % d
+
+    offset = get_offset(indent)
+    return offset + ("\n" + offset).join(block.split("\n"))
+
+def output_plugin(plugin, indent=0):
+    print "PLUGIN", plugin.get_name()
+    version = plugin.get_version()
+    
+    elements = {}
+    gst.debug('getting features for plugin %s' % plugin.get_name())
+    registry = gst.registry_get_default()
+    features = registry.get_feature_list_by_plugin(plugin.get_name())
+    gst.debug('plugin %s has %d features' % (plugin.get_name(), len(features)))
+    for feature in features:
+        if isinstance(feature, gst.ElementFactory):
+            elements[feature.get_name()] = feature
+    #gst.debug("got features")
+        
+    elementsoutput = []
+    keys = elements.keys()
+    keys.sort()
+    for name in keys:
+        feature = elements[name]
+        elementsoutput.append(output_element_factory(feature, indent + 2))
+
+    filename = plugin.get_filename()
+    basename = filename
+    if basename:
+        basename = os.path.basename(basename)
+    d = {
+        'name':        xmlencode(plugin.get_name()),
+        'description': xmlencode(plugin.get_description()),
+        'filename':    filename,
+        'basename':    basename,
+        'version':     version,
+        'license':     xmlencode(plugin.get_license()),
+        'source':      xmlencode(plugin.get_source()),
+        'package':     xmlencode(plugin.get_package()),
+        'origin':      xmlencode(plugin.get_origin()),
+        'elements': "\n".join(elementsoutput),
+    }
+    block = PLUGIN_TEMPLATE % d
+    
+    offset = get_offset(indent)
+    return offset + ("\n" + offset).join(block.split("\n"))
+
+def main():
+    if len(sys.argv) == 1:
+        sys.stderr.write("Please specify a source module to inspect")
+        sys.exit(1)
+    source = sys.argv[1]
+
+    if len(sys.argv) > 2:
+        os.chdir(sys.argv[2])
+
+    registry = gst.registry_get_default()
+    all = registry.get_plugin_list()
+    for plugin in all:
+        gst.debug("inspecting plugin %s from source %s" % (
+            plugin.get_name(), plugin.get_source()))
+        # this skips gstcoreelements, with bin and pipeline
+        if plugin.get_filename() is None:
+            continue
+        if plugin.get_source() != source:
+            continue
+
+        filename = "plugin-%s.xml" % plugin.get_name()
+        handle = open(filename, "w")
+        handle.write(output_plugin(plugin))
+        handle.close()
+
+main()
diff --git a/common/gstdoc-scangobj b/common/gstdoc-scangobj
new file mode 100755
index 0000000..cb7b731
--- /dev/null
+++ b/common/gstdoc-scangobj
@@ -0,0 +1,1644 @@
+#!/usr/bin/perl -w
+# -*- cperl -*-
+#
+# gtk-doc - GTK DocBook documentation generator.
+# Copyright (C) 1998  Damon Chaplin
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+#
+# This gets information about object heirarchies and signals
+# by compiling a small C program. CFLAGS and LDFLAGS must be
+# set appropriately before running this script.
+#
+# NOTE: the lookup_signal_arg_names() function contains the argument names of
+#       standard GTK signal handlers. This may need to be updated for new
+#       GTK signals or Gnome widget signals.
+
+use Getopt::Long;
+
+unshift @INC, '/usr/share/gtk-doc/data';
+require "gtkdoc-common.pl";
+
+# Options
+
+# name of documentation module
+my $MODULE;
+my $OUTPUT_DIR;
+my $PRINT_VERSION;
+my $PRINT_HELP;
+my $TYPE_INIT_FUNC="g_type_init ()";
+
+# --nogtkinit is deprecated, as it is the default now anyway.
+%optctl = (module => \$MODULE,
+           source => \$SOURCE,
+	   types => \$TYPES_FILE,
+	   nogtkinit => \$NO_GTK_INIT,
+	   'type-init-func' => \$TYPE_INIT_FUNC,
+	   'output-dir' => \$OUTPUT_DIR,
+	   'version' => \$PRINT_VERSION,
+	   'help' => \$PRINT_HELP);
+	   
+GetOptions(\%optctl, "module=s", "source=s", "types:s", "output-dir:s", "nogtkinit", "type-init-func:s", "version", "help");
+
+if ($NO_GTK_INIT) {
+  # Do nothing. This just avoids a warning.
+}
+
+if ($PRINT_VERSION) {
+    print "1.5\n";
+    exit 0;
+}
+
+if (!$MODULE) {
+    $PRINT_HELP = 1;
+}
+
+if ($PRINT_HELP) {
+    print "gstdoc-scangobj version 1.5\n";
+    print "\n--module=MODULE_NAME  Name of the doc module being parsed";
+    print "\n--source=SOURCE_NAME  Name of the source module for plugins";
+    print "\n--types=FILE          The name of the file to store the types in";
+    print "\n--type-init-func=FUNC The init function to call instead of g_type_init ()";
+    print "\n--output-dir=DIRNAME  The directory where the results are stored";
+    print "\n--version             Print the version of this program";
+    print "\n--help                Print this help\n";
+    exit 0;
+}
+
+$OUTPUT_DIR = $OUTPUT_DIR ? $OUTPUT_DIR : ".";
+
+$TYPES_FILE = $TYPES_FILE ? $TYPES_FILE : "$OUTPUT_DIR/$MODULE.types";
+
+open (TYPES, $TYPES_FILE) || die "Cannot open $TYPES_FILE: $!\n";
+open (OUTPUT, ">$MODULE-scan.c") || die "Cannot open $MODULE-scan.c: $!\n";
+
+my $old_signals_filename = "$OUTPUT_DIR/$MODULE.signals";
+my $new_signals_filename = "$OUTPUT_DIR/$MODULE.signals.new";
+my $old_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy";
+my $new_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy.new";
+my $old_interfaces_filename = "$OUTPUT_DIR/$MODULE.interfaces";
+my $new_interfaces_filename = "$OUTPUT_DIR/$MODULE.interfaces.new";
+my $old_prerequisites_filename = "$OUTPUT_DIR/$MODULE.prerequisites";
+my $new_prerequisites_filename = "$OUTPUT_DIR/$MODULE.prerequisites.new";
+my $old_args_filename = "$OUTPUT_DIR/$MODULE.args";
+my $new_args_filename = "$OUTPUT_DIR/$MODULE.args.new";
+
+# write a C program to scan the types
+
+$includes = "";
+ at types = ();
+ at impl_types = ();
+
+for (<TYPES>) {
+    if (/^#include/) {
+	$includes .= $_;
+    } elsif (/^%/) {
+	next;
+    } elsif (/^\s*$/) {
+	next;
+    } elsif (/^type:(.*)$/) {
+	$t = $1;
+        chomp $t;
+	push @impl_types, $t;
+    } else {
+	chomp;
+	push @types, $_;
+    }
+}
+
+$ntypes = @types + @impl_types;
+
+print OUTPUT <<EOT;
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+$includes
+
+#ifdef GTK_IS_WIDGET_CLASS
+#include <gtk/gtkversion.h>
+#endif
+GType *object_types = NULL;
+
+static GType *
+get_object_types (void)
+{
+    GList *plugins = NULL;
+    GList *factories = NULL;
+    GList *l;
+    GstElementFactory *factory = NULL;
+    GType type;
+
+    gint i = 0;
+
+    /* get a list of features from plugins in our source module */
+    plugins = gst_registry_get_plugin_list (gst_registry_get_default());
+
+    while (plugins) {
+      GList *features;
+      GstPlugin *plugin;
+      const gchar *source;
+
+      plugin = (GstPlugin *) (plugins->data);
+      plugins = g_list_next (plugins);
+      source = gst_plugin_get_source (plugin);
+      /*g_print ("plugin: %s source: %s\\n", plugin->desc.name, source);*/
+      if (!source || strcmp (source, "$SOURCE") != 0) {
+        continue;
+      }
+      g_print ("plugin: %s source: %s\\n", plugin->desc.name, source);
+
+      features =
+          gst_registry_get_feature_list_by_plugin (gst_registry_get_default (),
+          plugin->desc.name);
+      while (features) {
+        GstPluginFeature *feature;
+        feature = GST_PLUGIN_FEATURE (features->data);
+        feature = gst_plugin_feature_load (feature);
+        if (!feature) {
+          g_warning ("Could not load plugin feature %s",
+                     gst_plugin_feature_get_name (feature));
+        }
+
+        if (GST_IS_ELEMENT_FACTORY (feature)) {
+          factory = GST_ELEMENT_FACTORY (feature);
+          factories = g_list_prepend (factories, factory);
+        }
+        features = g_list_next (features);
+      }
+    }
+
+    g_message ("number of element factories: %d", g_list_length (factories));
+
+    /* allocate the object_types array to hold them */
+    object_types = g_new0 (GType, g_list_length (factories)+$ntypes+1);
+
+    l = factories;
+    i = 0;
+
+    /* fill it */
+    while (l) {
+      factory = GST_ELEMENT_FACTORY (l->data);
+      type = gst_element_factory_get_element_type (factory);
+      if (type != 0) {
+        g_message ("adding type %p for factory %s", (void *) type, gst_element_factory_get_longname (factory));
+        object_types[i++] = type;
+      } else {
+        g_message ("type info for factory %s not found",
+            gst_element_factory_get_longname (factory));
+      }
+      l = g_list_next (l);
+    }
+
+EOT
+
+# get_type functions:
+for (@types) {
+print OUTPUT <<EOT;
+    type = $_ ();
+    if (type == 0) {
+      g_message ("$_ () didn't return a valid type");
+    }
+    else {
+      object_types[i++] = type;
+    }
+EOT
+}
+
+# Implicit types retrieved from GLib:
+for (@impl_types) {
+print OUTPUT <<EOT;
+    type = g_type_from_name ("$_");
+    if (type == 0) {
+      g_message ("Implicit type $_ not found");
+    }
+    else {
+      object_types[i++] = type;
+    }
+EOT
+}
+
+print OUTPUT <<EOT;
+
+    object_types[i] = 0;
+
+    /* Need to make sure all the types are loaded in and initialize
+     * their signals and properties.
+     */
+    for (i=0; object_types[i]; i++) {
+      if (G_TYPE_IS_CLASSED (object_types[i]))
+        g_type_class_ref (object_types[i]);
+        else {
+          g_warning ("not reffing type: %s", g_type_name (object_types[i]));
+        }
+    }
+
+    return object_types;
+}
+
+/*
+ * This uses GObject type functions to output signal prototypes and the object
+ * hierarchy.
+ */
+
+/* The output files */
+const gchar *signals_filename = "$new_signals_filename";
+const gchar *hierarchy_filename = "$new_hierarchy_filename";
+const gchar *interfaces_filename = "$new_interfaces_filename";
+const gchar *prerequisites_filename = "$new_prerequisites_filename";
+const gchar *args_filename = "$new_args_filename";
+
+
+static void output_signals (void);
+static void output_object_signals (FILE *fp,
+				   GType object_type);
+static void output_object_signal (FILE *fp,
+				  const gchar *object_class_name,
+				  guint signal_id);
+static const gchar * get_type_name (GType type,
+			            gboolean * is_pointer);
+static const gchar * get_gdk_event (const gchar * signal_name);
+static const gchar ** lookup_signal_arg_names (const gchar * type,
+					       const gchar * signal_name);
+
+static void output_object_hierarchy (void);
+static void output_hierarchy (FILE *fp,
+			      GType type,
+			      guint level);
+
+static void output_object_interfaces (void);
+static void output_interfaces (FILE *fp,
+			       GType type);
+
+static void output_interface_prerequisites (void);
+static void output_prerequisites (FILE *fp,
+			          GType type);
+
+static void output_args (void);
+static void output_object_args (FILE *fp, GType object_type);
+
+int
+main (int argc, char *argv[])
+{
+  /* Silence the compiler: */
+  if (argv != argv) argc = argc;
+
+  $TYPE_INIT_FUNC;
+
+  get_object_types ();
+
+  output_signals ();
+  output_object_hierarchy ();
+  output_object_interfaces ();
+  output_interface_prerequisites ();
+  output_args ();
+
+  return 0;
+}
+
+
+static void
+output_signals (void)
+{
+  FILE *fp;
+  gint i;
+
+  fp = fopen (signals_filename, "w");
+  if (fp == NULL)
+    {
+      g_warning ("Couldn't open output file: %s : %s", signals_filename, strerror(errno));
+      return;
+    }
+
+  for (i = 0; object_types[i]; i++)
+    output_object_signals (fp, object_types[i]);
+
+  fclose (fp);
+}
+
+static gint
+compare_signals (const void *a, const void *b)
+{
+  const guint *signal_a = a;
+  const guint *signal_b = b;
+
+  return strcmp (g_signal_name (*signal_a), g_signal_name (*signal_b));
+}
+
+/* This outputs all the signals of one object. */
+static void
+output_object_signals (FILE *fp, GType object_type)
+{
+  const gchar *object_class_name;
+  guint *signals, n_signals;
+  guint sig;
+
+  if (G_TYPE_IS_INSTANTIATABLE (object_type) ||
+      G_TYPE_IS_INTERFACE (object_type))
+    {
+
+      object_class_name = g_type_name (object_type);
+
+      signals = g_signal_list_ids (object_type, &n_signals);
+      qsort (signals, n_signals, sizeof (guint), compare_signals);
+
+      for (sig = 0; sig < n_signals; sig++)
+        {
+           output_object_signal (fp, object_class_name, signals[sig]);
+        }
+      g_free (signals);
+   }
+}
+
+
+/* This outputs one signal. */
+static void
+output_object_signal (FILE *fp,
+		      const gchar *object_name,
+		      guint signal_id)
+{
+  GSignalQuery query_info;
+  const gchar *type_name, *ret_type, *object_arg, *arg_name;
+  gchar *pos, *object_arg_lower;
+  gboolean is_pointer;
+  gchar ret_type_buffer[1024], buffer[1024];
+  guint i, param;
+  const gchar **arg_names;
+  gint param_num, widget_num, event_num, callback_num;
+  gint *arg_num;
+  gchar signal_name[128];
+  gchar flags[16];
+
+  /*  g_print ("Object: %s Signal: %u\\n", object_name, signal_id);*/
+
+  param_num = 1;
+  widget_num = event_num = callback_num = 0;
+
+  g_signal_query (signal_id, &query_info);
+
+  /* Output the return type and function name. */
+  ret_type = get_type_name (query_info.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer);
+  sprintf (ret_type_buffer, "%s%s", ret_type, is_pointer ? "*" : "");
+
+  /* Output the signal object type and the argument name. We assume the
+     type is a pointer - I think that is OK. We remove "Gtk" or "Gnome" and
+     convert to lower case for the argument name. */
+  pos = buffer;
+  sprintf (pos, "%s ", object_name);
+  pos += strlen (pos);
+
+  if (!strncmp (object_name, "Gtk", 3))
+      object_arg = object_name + 3;
+  else if (!strncmp (object_name, "Gnome", 5))
+      object_arg = object_name + 5;
+  else
+      object_arg = object_name;
+
+  object_arg_lower = g_ascii_strdown (object_arg, -1);
+  sprintf (pos, "*%s\\n", object_arg_lower);
+  pos += strlen (pos);
+  if (!strncmp (object_arg_lower, "widget", 6))
+    widget_num = 2;
+  g_free(object_arg_lower);
+
+  /* Convert signal name to use underscores rather than dashes '-'. */
+  strcpy (signal_name, query_info.signal_name);
+  for (i = 0; signal_name[i]; i++)
+    {
+      if (signal_name[i] == '-')
+	signal_name[i] = '_';
+    }
+
+  /* Output the signal parameters. */
+  arg_names = lookup_signal_arg_names (object_name, signal_name);
+
+  for (param = 0; param < query_info.n_params; param++)
+    {
+      if (arg_names)
+	{
+	  sprintf (pos, "%s\\n", arg_names[param]);
+	  pos += strlen (pos);
+	}
+      else
+	{
+	  type_name = get_type_name (query_info.param_types[param] & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer);
+
+	  /* Most arguments to the callback are called "arg1", "arg2", etc.
+	     GdkWidgets are called "widget", "widget2", ...
+	     GdkEvents are called "event", "event2", ...
+	     GtkCallbacks are called "callback", "callback2", ... */
+	  if (!strcmp (type_name, "GtkWidget"))
+	    {
+	      arg_name = "widget";
+	      arg_num = &widget_num;
+	    }
+	  else if (!strcmp (type_name, "GdkEvent"))
+	    {
+	      type_name = get_gdk_event (signal_name);
+	      arg_name = "event";
+	      arg_num = &event_num;
+	      is_pointer = TRUE;
+	    }
+	  else if (!strcmp (type_name, "GtkCallback")
+		   || !strcmp (type_name, "GtkCCallback"))
+	    {
+	      arg_name = "callback";
+	      arg_num = &callback_num;
+	    }
+	  else
+	    {
+	      arg_name = "arg";
+	      arg_num = &param_num;
+	    }
+	  sprintf (pos, "%s ", type_name);
+	  pos += strlen (pos);
+
+	  if (!arg_num || *arg_num == 0)
+	    sprintf (pos, "%s%s\\n", is_pointer ? "*" : " ", arg_name);
+	  else
+	    sprintf (pos, "%s%s%i\\n", is_pointer ? "*" : " ", arg_name,
+		     *arg_num);
+	  pos += strlen (pos);
+
+	  if (arg_num)
+	    {
+	      if (*arg_num == 0)
+		*arg_num = 2;
+	      else
+	        *arg_num += 1;
+	    }
+	}
+    }
+
+  pos = flags;
+  /* We use one-character flags for simplicity. */
+  if (query_info.signal_flags & G_SIGNAL_RUN_FIRST)
+    *pos++ = 'f';
+  if (query_info.signal_flags & G_SIGNAL_RUN_LAST)
+    *pos++ = 'l';
+  if (query_info.signal_flags & G_SIGNAL_RUN_CLEANUP)
+    *pos++ = 'c';
+  if (query_info.signal_flags & G_SIGNAL_NO_RECURSE)
+    *pos++ = 'r';
+  if (query_info.signal_flags & G_SIGNAL_DETAILED)
+    *pos++ = 'd';
+  if (query_info.signal_flags & G_SIGNAL_ACTION)
+    *pos++ = 'a';
+  if (query_info.signal_flags & G_SIGNAL_NO_HOOKS)
+    *pos++ = 'h';
+  *pos = 0;
+
+  fprintf (fp,
+	   "<SIGNAL>\\n<NAME>%s::%s</NAME>\\n<RETURNS>%s</RETURNS>\\n<FLAGS>%s</FLAGS>\\n%s</SIGNAL>\\n\\n",
+	   object_name, query_info.signal_name, ret_type_buffer, flags, buffer);
+}
+
+
+/* Returns the type name to use for a signal argument or return value, given
+   the GtkType from the signal info. It also sets is_pointer to TRUE if the
+   argument needs a '*' since it is a pointer. */
+static const gchar *
+get_type_name (GType type, gboolean * is_pointer)
+{
+  const gchar *type_name;
+
+  *is_pointer = FALSE;
+  type_name = g_type_name (type);
+
+  switch (type) {
+  case G_TYPE_NONE:
+  case G_TYPE_CHAR:
+  case G_TYPE_UCHAR:
+  case G_TYPE_BOOLEAN:
+  case G_TYPE_INT:
+  case G_TYPE_UINT:
+  case G_TYPE_LONG:
+  case G_TYPE_ULONG:
+  case G_TYPE_FLOAT:
+  case G_TYPE_DOUBLE:
+  case G_TYPE_POINTER:
+    /* These all have normal C type names so they are OK. */
+    return type_name;
+
+  case G_TYPE_STRING:
+    /* A GtkString is really a gchar*. */
+    *is_pointer = TRUE;
+    return "gchar";
+
+  case G_TYPE_ENUM:
+  case G_TYPE_FLAGS:
+    /* We use a gint for both of these. Hopefully a subtype with a decent
+       name will be registered and used instead, as GTK+ does itself. */
+    return "gint";
+
+  case G_TYPE_BOXED:
+    /* The boxed type shouldn't be used itself, only subtypes. Though we
+       return 'gpointer' just in case. */
+    return "gpointer";
+
+  case G_TYPE_PARAM:
+    /* A GParam is really a GParamSpec*. */
+    *is_pointer = TRUE;
+    return "GParamSpec";
+
+  default:
+    break;
+  }
+
+  /* For all GObject subclasses we can use the class name with a "*",
+     e.g. 'GtkWidget *'. */
+  if (g_type_is_a (type, G_TYPE_OBJECT))
+    *is_pointer = TRUE;
+
+  if (G_TYPE_IS_CLASSED (type))
+    *is_pointer = TRUE;
+
+  /* All boxed subtypes will be pointers as well. */
+  if (g_type_is_a (type, G_TYPE_BOXED))
+    *is_pointer = TRUE;
+
+  /* All pointer subtypes will be pointers as well. */
+  if (g_type_is_a (type, G_TYPE_POINTER))
+    *is_pointer = TRUE;
+
+  /* But enums are not */
+  if (g_type_is_a (type, G_TYPE_ENUM) ||
+      g_type_is_a (type, G_TYPE_FLAGS))
+    *is_pointer = FALSE;
+
+  return type_name;
+}
+
+
+static const gchar *
+get_gdk_event (const gchar * signal_name)
+{
+  static const gchar *GbGDKEvents[] =
+  {
+    "button_press_event", "GdkEventButton",
+    "button_release_event", "GdkEventButton",
+    "motion_notify_event", "GdkEventMotion",
+    "delete_event", "GdkEvent",
+    "destroy_event", "GdkEvent",
+    "expose_event", "GdkEventExpose",
+    "key_press_event", "GdkEventKey",
+    "key_release_event", "GdkEventKey",
+    "enter_notify_event", "GdkEventCrossing",
+    "leave_notify_event", "GdkEventCrossing",
+    "configure_event", "GdkEventConfigure",
+    "focus_in_event", "GdkEventFocus",
+    "focus_out_event", "GdkEventFocus",
+    "map_event", "GdkEvent",
+    "unmap_event", "GdkEvent",
+    "property_notify_event", "GdkEventProperty",
+    "selection_clear_event", "GdkEventSelection",
+    "selection_request_event", "GdkEventSelection",
+    "selection_notify_event", "GdkEventSelection",
+    "proximity_in_event", "GdkEventProximity",
+    "proximity_out_event", "GdkEventProximity",
+    "drag_begin_event", "GdkEventDragBegin",
+    "drag_request_event", "GdkEventDragRequest",
+    "drag_end_event", "GdkEventDragRequest",
+    "drop_enter_event", "GdkEventDropEnter",
+    "drop_leave_event", "GdkEventDropLeave",
+    "drop_data_available_event", "GdkEventDropDataAvailable",
+    "other_event", "GdkEventOther",
+    "client_event", "GdkEventClient",
+    "no_expose_event", "GdkEventNoExpose",
+    "visibility_notify_event", "GdkEventVisibility",
+    "window_state_event", "GdkEventWindowState",
+    "scroll_event", "GdkEventScroll",
+    NULL
+  };
+
+  gint i;
+
+  for (i = 0; GbGDKEvents[i]; i += 2)
+    {
+      if (!strcmp (signal_name, GbGDKEvents[i]))
+	return GbGDKEvents[i + 1];
+    }
+  return "GdkEvent";
+}
+
+
+/* This returns argument names to use for some known GTK signals.
+    It is passed a widget name, e.g. 'GtkCList' and a signal name, e.g.
+    'select_row' and it returns a pointer to an array of argument types and
+    names. */
+static const gchar **
+lookup_signal_arg_names (const gchar * type, const gchar * signal_name)
+{
+  /* Each arg array starts with the object type name and the signal name,
+     and then signal arguments follow. */
+  static const gchar *GbArgTable[][16] =
+  {
+    {"GtkCList", "select_row",
+     "gint             row",
+     "gint             column",
+     "GdkEventButton  *event"},
+    {"GtkCList", "unselect_row",
+     "gint             row",
+     "gint             column",
+     "GdkEventButton  *event"},
+    {"GtkCList", "click_column",
+     "gint             column"},
+
+    {"GtkCList", "resize_column",
+     "gint             column",
+     "gint             width"},
+
+    {"GtkCList", "extend_selection",
+     "GtkScrollType    scroll_type",
+     "gfloat           position",
+     "gboolean         auto_start_selection"},
+    {"GtkCList", "scroll_vertical",
+     "GtkScrollType    scroll_type",
+     "gfloat           position"},
+    {"GtkCList", "scroll_horizontal",
+     "GtkScrollType    scroll_type",
+     "gfloat           position"},
+
+    {"GtkCTree", "tree_select_row",
+     "GtkCTreeNode    *node",
+     "gint             column"},
+    {"GtkCTree", "tree_unselect_row",
+     "GtkCTreeNode    *node",
+     "gint             column"},
+    {"GtkCTree", "tree_expand",
+     "GtkCTreeNode    *node"},
+    {"GtkCTree", "tree_collapse",
+     "GtkCTreeNode    *node"},
+    {"GtkCTree", "tree_move",
+     "GtkCTreeNode    *node",
+     "GtkCTreeNode    *new_parent",
+     "GtkCTreeNode    *new_sibling"},
+    {"GtkCTree", "change_focus_row_expansion",
+     "GtkCTreeExpansionType expansion"},
+
+    {"GtkEditable", "insert_text",
+     "gchar           *new_text",
+     "gint             new_text_length",
+     "gint            *position"},
+    {"GtkEditable", "delete_text",
+     "gint             start_pos",
+     "gint             end_pos"},
+    {"GtkEditable", "set_editable",
+     "gboolean         is_editable"},
+    {"GtkEditable", "move_cursor",
+     "gint             x",
+     "gint             y"},
+    {"GtkEditable", "move_word",
+     "gint             num_words"},
+    {"GtkEditable", "move_page",
+     "gint             x",
+     "gint             y"},
+    {"GtkEditable", "move_to_row",
+     "gint             row"},
+    {"GtkEditable", "move_to_column",
+     "gint             column"},
+
+    {"GtkEditable", "kill_char",
+     "gint             direction"},
+    {"GtkEditable", "kill_word",
+     "gint             direction"},
+    {"GtkEditable", "kill_line",
+     "gint             direction"},
+
+
+    {"GtkInputDialog", "enable_device",
+     "GdkDevice       *deviceid"},
+    {"GtkInputDialog", "disable_device",
+     "GdkDevice       *deviceid"},
+
+    {"GtkListItem", "extend_selection",
+     "GtkScrollType    scroll_type",
+     "gfloat           position",
+     "gboolean         auto_start_selection"},
+    {"GtkListItem", "scroll_vertical",
+     "GtkScrollType    scroll_type",
+     "gfloat           position"},
+    {"GtkListItem", "scroll_horizontal",
+     "GtkScrollType    scroll_type",
+     "gfloat           position"},
+
+    {"GtkMenuShell", "move_current",
+     "GtkMenuDirectionType direction"},
+    {"GtkMenuShell", "activate_current",
+     "gboolean         force_hide"},
+
+
+    {"GtkNotebook", "switch_page",
+     "GtkNotebookPage *page",
+     "guint            page_num"},
+    {"GtkStatusbar", "text_pushed",
+     "guint            context_id",
+     "gchar           *text"},
+    {"GtkStatusbar", "text_popped",
+     "guint            context_id",
+     "gchar           *text"},
+    {"GtkTipsQuery", "widget_entered",
+     "GtkWidget       *widget",
+     "gchar           *tip_text",
+     "gchar           *tip_private"},
+    {"GtkTipsQuery", "widget_selected",
+     "GtkWidget       *widget",
+     "gchar           *tip_text",
+     "gchar           *tip_private",
+     "GdkEventButton  *event"},
+    {"GtkToolbar", "orientation_changed",
+     "GtkOrientation   orientation"},
+    {"GtkToolbar", "style_changed",
+     "GtkToolbarStyle  style"},
+    {"GtkWidget", "draw",
+     "GdkRectangle    *area"},
+    {"GtkWidget", "size_request",
+     "GtkRequisition  *requisition"},
+    {"GtkWidget", "size_allocate",
+     "GtkAllocation   *allocation"},
+    {"GtkWidget", "state_changed",
+     "GtkStateType     state"},
+    {"GtkWidget", "style_set",
+     "GtkStyle        *previous_style"},
+
+    {"GtkWidget", "install_accelerator",
+     "gchar           *signal_name",
+     "gchar            key",
+     "gint             modifiers"},
+
+    {"GtkWidget", "add_accelerator",
+     "guint            accel_signal_id",
+     "GtkAccelGroup   *accel_group",
+     "guint            accel_key",
+     "GdkModifierType  accel_mods",
+     "GtkAccelFlags    accel_flags"},
+
+    {"GtkWidget", "parent_set",
+     "GtkObject       *old_parent"},
+
+    {"GtkWidget", "remove_accelerator",
+     "GtkAccelGroup   *accel_group",
+     "guint            accel_key",
+     "GdkModifierType  accel_mods"},
+    {"GtkWidget", "debug_msg",
+     "gchar           *message"},
+    {"GtkWindow", "move_resize",
+     "gint            *x",
+     "gint            *y",
+     "gint             width",
+     "gint             height"},
+    {"GtkWindow", "set_focus",
+     "GtkWidget       *widget"},
+
+    {"GtkWidget", "selection_get",
+     "GtkSelectionData *data",
+     "guint            info",
+     "guint            time"},
+    {"GtkWidget", "selection_received",
+     "GtkSelectionData *data",
+     "guint            time"},
+
+    {"GtkWidget", "drag_begin",
+     "GdkDragContext  *drag_context"},
+    {"GtkWidget", "drag_end",
+     "GdkDragContext  *drag_context"},
+    {"GtkWidget", "drag_data_delete",
+     "GdkDragContext  *drag_context"},
+    {"GtkWidget", "drag_leave",
+     "GdkDragContext  *drag_context",
+     "guint            time"},
+    {"GtkWidget", "drag_motion",
+     "GdkDragContext  *drag_context",
+     "gint             x",
+     "gint             y",
+     "guint            time"},
+    {"GtkWidget", "drag_drop",
+     "GdkDragContext  *drag_context",
+     "gint             x",
+     "gint             y",
+     "guint            time"},
+    {"GtkWidget", "drag_data_get",
+     "GdkDragContext  *drag_context",
+     "GtkSelectionData *data",
+     "guint            info",
+     "guint            time"},
+    {"GtkWidget", "drag_data_received",
+     "GdkDragContext  *drag_context",
+     "gint             x",
+     "gint             y",
+     "GtkSelectionData *data",
+     "guint            info",
+     "guint            time"},
+
+    {NULL}
+  };
+
+  gint i;
+
+  for (i = 0; GbArgTable[i][0]; i++)
+    {
+#if 1
+      if (!strcmp (type, GbArgTable[i][0])
+	  && !strcmp (signal_name, GbArgTable[i][1]))
+	return &GbArgTable[i][2];
+#endif
+    }
+  return NULL;
+}
+
+/* This outputs the hierarchy of all objects which have been initialized,
+   i.e. by calling their XXX_get_type() initialization function. */
+static void
+output_object_hierarchy (void)
+{
+  FILE *fp;
+  gint i;
+
+  fp = fopen (hierarchy_filename, "w");
+  if (fp == NULL)
+    {
+      g_warning ("Couldn't open output file: %s : %s", hierarchy_filename, strerror(errno));
+      return;
+    }
+  output_hierarchy (fp, G_TYPE_OBJECT, 0);
+  output_hierarchy (fp, G_TYPE_INTERFACE, 0);
+  
+  for (i=0; object_types[i]; i++) {
+    if (!g_type_parent (object_types[i]) &&
+      (object_types[i] != G_TYPE_NONE) &&
+      (object_types[i] != G_TYPE_OBJECT) &&
+      (object_types[i] != G_TYPE_INTERFACE)
+    ) {
+      g_warning ("printing hierarchy for root type: %s",
+          g_type_name (object_types[i]));
+      output_hierarchy (fp, object_types[i], 0);
+    }
+  }
+  /* for debugging
+  for (i=0; object_types[i]; i++) {
+    if(object_types[i] != G_TYPE_NONE) {
+      g_print ("type has not been added to hierarchy: %s\\n",
+        g_type_name (object_types[i]));
+    }
+  }
+  for debugging */
+
+  fclose (fp);
+}
+
+/* This is called recursively to output the hierarchy of a widget. */
+static void
+output_hierarchy (FILE  *fp,
+		  GType  type,
+		  guint   level)
+{
+  guint i;
+  GType *children;
+  guint n_children;
+
+  if (!type)
+    return;
+
+  /* for debugging
+  for (i=0; object_types[i]; i++) {
+    if(object_types[i] == type) {
+      g_print ("added type to hierarchy (level %d): %s\\n",
+        level, g_type_name (type));
+      object_types[i] = G_TYPE_NONE;
+      break;
+    }
+  }
+  for debugging */
+
+  for (i = 0; i < level; i++)
+    fprintf (fp, "  ");
+  fprintf (fp, "%s", g_type_name (type));
+  fprintf (fp, "\\n");
+
+  children = g_type_children (type, &n_children);
+
+  for (i=0; i < n_children; i++) {
+    output_hierarchy (fp, children[i], level + 1);
+  }
+
+  g_free (children);
+}
+
+static void output_object_interfaces (void)
+{
+  guint i;
+  FILE *fp;
+
+  fp = fopen (interfaces_filename, "w");
+  if (fp == NULL)
+    {
+      g_warning ("Couldn't open output file: %s : %s", interfaces_filename, strerror(errno));
+      return;
+    }
+  output_interfaces (fp, G_TYPE_OBJECT);
+
+  for (i = 0; object_types[i]; i++)
+    {
+      if (!g_type_parent (object_types[i]) &&
+          (object_types[i] != G_TYPE_OBJECT) &&
+          G_TYPE_IS_INSTANTIATABLE (object_types[i]))
+        {
+          output_interfaces (fp, object_types[i]);
+        }
+    }
+  fclose (fp);
+}
+
+static void
+output_interfaces (FILE  *fp,
+		   GType  type)
+{
+  guint i;
+  GType *children, *interfaces;
+  guint n_children, n_interfaces;
+
+  if (!type)
+    return;
+
+  interfaces = g_type_interfaces (type, &n_interfaces);
+
+  if (n_interfaces > 0)
+    {
+      fprintf (fp, "%s", g_type_name (type));
+      for (i=0; i < n_interfaces; i++)
+          fprintf (fp, " %s", g_type_name (interfaces[i]));
+      fprintf (fp, "\\n");
+     }
+  g_free (interfaces);
+
+  children = g_type_children (type, &n_children);
+
+  for (i=0; i < n_children; i++)
+    output_interfaces (fp, children[i]);
+
+  g_free (children);
+}
+
+static void output_interface_prerequisites (void)
+{
+  FILE *fp;
+
+  fp = fopen (prerequisites_filename, "w");
+  if (fp == NULL)
+    {
+      g_warning ("Couldn't open output file: %s : %s", prerequisites_filename, strerror(errno));
+      return;
+    }
+  output_prerequisites (fp, G_TYPE_INTERFACE);
+  fclose (fp);
+}
+
+static void
+output_prerequisites (FILE  *fp,
+		      GType  type)
+{
+#if GLIB_CHECK_VERSION(2,1,0)
+  guint i;
+  GType *children, *prerequisites;
+  guint n_children, n_prerequisites;
+
+  if (!type)
+    return;
+
+  prerequisites = g_type_interface_prerequisites (type, &n_prerequisites);
+
+  if (n_prerequisites > 0)
+    {
+      fprintf (fp, "%s", g_type_name (type));
+      for (i=0; i < n_prerequisites; i++)
+          fprintf (fp, " %s", g_type_name (prerequisites[i]));
+      fprintf (fp, "\\n");
+     }
+  g_free (prerequisites);
+
+  children = g_type_children (type, &n_children);
+
+  for (i=0; i < n_children; i++)
+    output_prerequisites (fp, children[i]);
+
+  g_free (children);
+#endif
+}
+
+static void
+output_args (void)
+{
+  FILE *fp;
+  gint i;
+
+  fp = fopen (args_filename, "w");
+  if (fp == NULL)
+    {
+      g_warning ("Couldn't open output file: %s : %s", args_filename, strerror(errno));
+      return;
+    }
+
+  for (i = 0; object_types[i]; i++) {
+    output_object_args (fp, object_types[i]);
+  }
+
+  fclose (fp);
+}
+
+static gint
+compare_param_specs (const void *a, const void *b)
+{
+  GParamSpec *spec_a = *(GParamSpec **)a;
+  GParamSpec *spec_b = *(GParamSpec **)b;
+
+  return strcmp (g_param_spec_get_name (spec_a), g_param_spec_get_name (spec_b));
+}
+
+/* Its common to have unsigned properties restricted
+ * to the signed range. Therefore we make this look
+ * a bit nicer by spelling out the max constants.
+ */
+
+/* Don't use "==" with floats, it might trigger a gcc warning.  */
+#define GTKDOC_COMPARE_FLOAT(x, y) (x <= y && x >= y)
+
+static gchar*
+describe_double_constant (gdouble value)
+{
+  gchar *desc;
+
+  if (GTKDOC_COMPARE_FLOAT (value, G_MAXDOUBLE))
+    desc = g_strdup ("G_MAXDOUBLE");
+  else if (GTKDOC_COMPARE_FLOAT (value, G_MINDOUBLE))
+    desc = g_strdup ("G_MINDOUBLE");
+  else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXDOUBLE))
+    desc = g_strdup ("-G_MAXDOUBLE");
+  else if (GTKDOC_COMPARE_FLOAT (value, G_MAXFLOAT))
+    desc = g_strdup ("G_MAXFLOAT");
+  else if (GTKDOC_COMPARE_FLOAT (value, G_MINFLOAT))
+    desc = g_strdup ("G_MINFLOAT");
+  else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXFLOAT))
+    desc = g_strdup ("-G_MAXFLOAT");
+  else
+    desc = g_strdup_printf ("%lg", value);
+
+  return desc;
+}
+
+static gchar*
+describe_signed_constant (gint64 value)
+{
+  gchar *desc;
+
+  if (value == G_MAXINT)
+    desc = g_strdup ("G_MAXINT");
+  else if (value == G_MININT)
+    desc = g_strdup ("G_MININT");
+  else if (value == G_MAXUINT)
+    desc = g_strdup ("G_MAXUINT");
+  else if (value == G_MAXLONG)
+    desc = g_strdup ("G_MAXLONG");
+  else if (value == G_MINLONG)
+    desc = g_strdup ("G_MINLONG");
+  else if (value == G_MAXULONG)
+    desc = g_strdup ("G_MAXULONG");
+  else if (value == G_MAXINT64)
+    desc = g_strdup ("G_MAXINT64");
+  else if (value == G_MININT64)
+    desc = g_strdup ("G_MININT64");
+  else
+    desc = g_strdup_printf ("%" G_GINT64_FORMAT, value);
+
+  return desc;
+}
+
+static gchar*
+describe_unsigned_constant (guint64 value)
+{
+  gchar *desc;
+
+  if (value == G_MAXINT)
+    desc = g_strdup ("G_MAXINT");
+  else if (value == G_MININT)
+    desc = g_strdup ("G_MININT");
+  else if (value == G_MAXUINT)
+    desc = g_strdup ("G_MAXUINT");
+  else if (value == G_MAXLONG)
+    desc = g_strdup ("G_MAXLONG");
+  else if (value == G_MINLONG)
+    desc = g_strdup ("G_MINLONG");
+  else if (value == G_MAXULONG)
+    desc = g_strdup ("G_MAXULONG");
+  else if (value == G_MAXINT64)
+    desc = g_strdup ("G_MAXINT64");
+  else if (value == G_MININT64)
+    desc = g_strdup ("G_MININT64");
+  else if (value == G_MAXUINT64)
+    desc = g_strdup ("G_MAXUINT64");
+  else
+    desc = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
+
+  return desc;
+}
+
+static gchar*
+describe_type (GParamSpec *spec)
+{
+  gchar *desc;
+  gchar *lower;
+  gchar *upper;
+
+  if (G_IS_PARAM_SPEC_CHAR (spec))
+    {
+      GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec);
+
+      lower = describe_signed_constant (pspec->minimum);
+      upper = describe_signed_constant (pspec->maximum);
+      if (pspec->minimum == G_MININT8 && pspec->maximum == G_MAXINT8)
+	desc = g_strdup ("");
+      else if (pspec->minimum == G_MININT8)
+	desc = g_strdup_printf ("<= %s", upper);
+      else if (pspec->maximum == G_MAXINT8)
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_UCHAR (spec))
+    {
+      GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec);
+
+      lower = describe_unsigned_constant (pspec->minimum);
+      upper = describe_unsigned_constant (pspec->maximum);
+      if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT8)
+	desc = g_strdup ("");
+      else if (pspec->minimum == 0)
+	desc = g_strdup_printf ("<= %s", upper);
+      else if (pspec->maximum == G_MAXUINT8)
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_INT (spec))
+    {
+      GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec);
+
+      lower = describe_signed_constant (pspec->minimum);
+      upper = describe_signed_constant (pspec->maximum);
+      if (pspec->minimum == G_MININT && pspec->maximum == G_MAXINT)
+	desc = g_strdup ("");
+      else if (pspec->minimum == G_MININT)
+	desc = g_strdup_printf ("<= %s", upper);
+      else if (pspec->maximum == G_MAXINT)
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_UINT (spec))
+    {
+      GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec);
+
+      lower = describe_unsigned_constant (pspec->minimum);
+      upper = describe_unsigned_constant (pspec->maximum);
+      if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT)
+	desc = g_strdup ("");
+      else if (pspec->minimum == 0)
+	desc = g_strdup_printf ("<= %s", upper);
+      else if (pspec->maximum == G_MAXUINT)
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_LONG (spec))
+    {
+      GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec);
+
+      lower = describe_signed_constant (pspec->minimum);
+      upper = describe_signed_constant (pspec->maximum);
+      if (pspec->minimum == G_MINLONG && pspec->maximum == G_MAXLONG)
+	desc = g_strdup ("");
+      else if (pspec->minimum == G_MINLONG)
+	desc = g_strdup_printf ("<= %s", upper);
+      else if (pspec->maximum == G_MAXLONG)
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_ULONG (spec))
+    {
+      GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec);
+      gchar *upper;
+
+      lower = describe_unsigned_constant (pspec->minimum);
+      upper = describe_unsigned_constant (pspec->maximum);
+      if (pspec->minimum == 0 && pspec->maximum == G_MAXULONG)
+	desc = g_strdup ("");
+      else if (pspec->minimum == 0)
+	desc = g_strdup_printf ("<= %s", upper);
+      else if (pspec->maximum == G_MAXULONG)
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_INT64 (spec))
+    {
+      GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec);
+
+      lower = describe_signed_constant (pspec->minimum);
+      upper = describe_signed_constant (pspec->maximum);
+      if (pspec->minimum == G_MININT64 && pspec->maximum == G_MAXINT64)
+	desc = g_strdup ("");
+      else if (pspec->minimum == G_MININT64)
+	desc = g_strdup_printf ("<= %s", upper);
+      else if (pspec->maximum == G_MAXINT64)
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_UINT64 (spec))
+    {
+      GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec);
+
+      lower = describe_unsigned_constant (pspec->minimum);
+      upper = describe_unsigned_constant (pspec->maximum);
+      if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT64)
+	desc = g_strdup ("");
+      else if (pspec->minimum == 0)
+	desc = g_strdup_printf ("<= %s", upper);
+      else if (pspec->maximum == G_MAXUINT64)
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_FLOAT (spec))
+    {
+      GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec);
+
+      lower = describe_double_constant (pspec->minimum);
+      upper = describe_double_constant (pspec->maximum);
+      if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXFLOAT))
+	{
+	  if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT))
+	    desc = g_strdup ("");
+	  else
+	    desc = g_strdup_printf ("<= %s", upper);
+	}
+      else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT))
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else if (G_IS_PARAM_SPEC_DOUBLE (spec))
+    {
+      GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec);
+
+      lower = describe_double_constant (pspec->minimum);
+      upper = describe_double_constant (pspec->maximum);
+      if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXDOUBLE))
+	{
+	  if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE))
+	    desc = g_strdup ("");
+	  else
+	    desc = g_strdup_printf ("<= %s", upper);
+	}
+      else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE))
+	desc = g_strdup_printf (">= %s", lower);
+      else
+	desc = g_strdup_printf ("[%s,%s]", lower, upper);
+      g_free (lower);
+      g_free (upper);
+    }
+  else
+    {
+      desc = g_strdup ("");
+    }
+
+  return desc;
+}
+
+static gchar*
+describe_default (GParamSpec *spec)
+{
+  gchar *desc;
+
+  if (G_IS_PARAM_SPEC_CHAR (spec))
+    {
+      GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec);
+
+      desc = g_strdup_printf ("%d", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_UCHAR (spec))
+    {
+      GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec);
+
+      desc = g_strdup_printf ("%u", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_BOOLEAN (spec))
+    {
+      GParamSpecBoolean *pspec = G_PARAM_SPEC_BOOLEAN (spec);
+
+      desc = g_strdup_printf ("%s", pspec->default_value ? "TRUE" : "FALSE");
+    }
+  else if (G_IS_PARAM_SPEC_INT (spec))
+    {
+      GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec);
+
+      desc = g_strdup_printf ("%d", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_UINT (spec))
+    {
+      GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec);
+
+      desc = g_strdup_printf ("%u", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_LONG (spec))
+    {
+      GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec);
+
+      desc = g_strdup_printf ("%ld", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_LONG (spec))
+    {
+      GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec);
+
+      desc = g_strdup_printf ("%lu", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_INT64 (spec))
+    {
+      GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec);
+
+      desc = g_strdup_printf ("%" G_GINT64_FORMAT, pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_UINT64 (spec))
+    {
+      GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec);
+
+      desc = g_strdup_printf ("%" G_GUINT64_FORMAT, pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_UNICHAR (spec))
+    {
+      GParamSpecUnichar *pspec = G_PARAM_SPEC_UNICHAR (spec);
+
+      if (g_unichar_isprint (pspec->default_value))
+	desc = g_strdup_printf ("'%c'", pspec->default_value);
+      else
+	desc = g_strdup_printf ("%u", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_ENUM (spec))
+    {
+      GParamSpecEnum *pspec = G_PARAM_SPEC_ENUM (spec);
+
+      GEnumValue *value = g_enum_get_value (pspec->enum_class, pspec->default_value);
+      if (value)
+	desc = g_strdup_printf ("%s", value->value_name);
+      else
+	desc = g_strdup_printf ("%d", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_FLAGS (spec))
+    {
+      GParamSpecFlags *pspec = G_PARAM_SPEC_FLAGS (spec);
+      guint default_value;
+      GString *acc;
+
+      default_value = pspec->default_value;
+      acc = g_string_new ("");
+
+      while (default_value)
+	{
+	  GFlagsValue *value = g_flags_get_first_value (pspec->flags_class, default_value);
+
+	  if (!value)
+	    break;
+
+	  if (acc->len > 0)
+	    g_string_append (acc, "|");
+	  g_string_append (acc, value->value_name);
+
+	  default_value &= ~value->value;
+	}
+
+      if (default_value == 0)
+	desc = g_string_free (acc, FALSE);
+      else
+	{
+	  desc = g_strdup_printf ("%d", pspec->default_value);
+	  g_string_free (acc, TRUE);
+	}
+    }
+  else if (G_IS_PARAM_SPEC_FLOAT (spec))
+    {
+      GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec);
+
+      desc = g_strdup_printf ("%g", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_DOUBLE (spec))
+    {
+      GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec);
+
+      desc = g_strdup_printf ("%lg", pspec->default_value);
+    }
+  else if (G_IS_PARAM_SPEC_STRING (spec))
+    {
+      GParamSpecString *pspec = G_PARAM_SPEC_STRING (spec);
+
+      if (pspec->default_value)
+	{
+	  gchar *esc = g_strescape (pspec->default_value, NULL);
+
+	  desc = g_strdup_printf ("\\"%s\\"", esc);
+
+	  g_free (esc);
+	}
+      else
+	desc = g_strdup_printf ("NULL");
+    }
+  else
+    {
+      desc = g_strdup ("");
+    }
+
+  return desc;
+}
+
+
+static void
+output_object_args (FILE *fp, GType object_type)
+{
+  gpointer class;
+  const gchar *object_class_name;
+  guint arg;
+  gchar flags[16], *pos;
+  GParamSpec **properties;
+  guint n_properties;
+  gboolean child_prop;
+  gboolean style_prop;
+  gboolean is_pointer;
+  const gchar *type_name;
+  gchar *type_desc;
+  gchar *default_value;
+
+  if (G_TYPE_IS_OBJECT (object_type))
+    {
+      class = g_type_class_peek (object_type);
+      if (!class)
+	return;
+
+      properties = g_object_class_list_properties (class, &n_properties);
+    }
+#if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 3)
+  else if (G_TYPE_IS_INTERFACE (object_type))
+    {
+      class = g_type_default_interface_ref (object_type);
+
+      if (!class)
+	return;
+
+      properties = g_object_interface_list_properties (class, &n_properties);
+    }
+#endif
+  else
+    return;
+
+  object_class_name = g_type_name (object_type);
+
+  child_prop = FALSE;
+  style_prop = FALSE;
+
+  while (TRUE) {
+    qsort (properties, n_properties, sizeof (GParamSpec *), compare_param_specs);
+    for (arg = 0; arg < n_properties; arg++)
+      {
+        GParamSpec *spec = properties[arg];
+        const gchar *nick, *blurb, *dot;
+
+        if (spec->owner_type != object_type)
+          continue;
+
+        pos = flags;
+        /* We use one-character flags for simplicity. */
+        if (child_prop && !style_prop)
+   	  *pos++ = 'c';
+        if (style_prop)
+   	  *pos++ = 's';
+        if (spec->flags & G_PARAM_READABLE)
+ 	  *pos++ = 'r';
+        if (spec->flags & G_PARAM_WRITABLE)
+	  *pos++ = 'w';
+        if (spec->flags & G_PARAM_CONSTRUCT)
+	  *pos++ = 'x';
+        if (spec->flags & G_PARAM_CONSTRUCT_ONLY)
+	  *pos++ = 'X';
+        *pos = 0;
+
+        nick = g_param_spec_get_nick (spec);
+        blurb = g_param_spec_get_blurb (spec);
+
+        dot = "";
+        if (blurb) {
+          int str_len = strlen (blurb);
+          if (str_len > 0  && blurb[str_len - 1] != '.')
+            dot = ".";
+        }
+
+	type_desc = describe_type (spec);
+	default_value = describe_default (spec);
+	type_name = get_type_name (spec->value_type, &is_pointer);
+        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",
+	         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);
+	g_free (type_desc);
+	g_free (default_value);
+      }
+
+    g_free (properties);
+
+#ifdef GTK_IS_CONTAINER_CLASS
+    if (!child_prop && GTK_IS_CONTAINER_CLASS (class)) {
+      properties = gtk_container_class_list_child_properties (class, &n_properties);
+      child_prop = TRUE;
+      continue;
+    }
+#endif
+
+#ifdef GTK_IS_WIDGET_CLASS
+#if GTK_CHECK_VERSION(2,1,0)
+    if (!style_prop && GTK_IS_WIDGET_CLASS (class)) {
+      properties = gtk_widget_class_list_style_properties (GTK_WIDGET_CLASS (class), &n_properties);
+      style_prop = TRUE;
+      continue;
+    }
+#endif
+#endif
+
+    break;
+  }
+}
+EOT
+
+close OUTPUT;
+
+# Compile and run our file
+
+$CC = $ENV{CC} ? $ENV{CC} : "gcc";
+$LD = $ENV{LD} ? $ENV{LD} : $CC;
+$CFLAGS = $ENV{CFLAGS} ? "$ENV{CFLAGS} -Wall -g" : "-Wall -g";
+$LDFLAGS = $ENV{LDFLAGS} ? $ENV{LDFLAGS} : "";
+
+my $o_file;
+if ($CC =~ /libtool/) {
+  $o_file  = "$MODULE-scan.lo"
+} else {
+  $o_file = "$MODULE-scan.o"
+}
+
+print "gtk-doc: Compiling scanner\n";
+$command = "$CC $CFLAGS -c -o $o_file $MODULE-scan.c";
+system($command) == 0 or die "Compilation of scanner failed: $!\n";
+
+print "gtk-doc: Linking scanner\n";
+$command = "$LD -o $MODULE-scan $o_file $LDFLAGS";
+system($command) == 0 or die "Linking of scanner failed: $!\n";
+
+print "gtk-doc: Running scanner $MODULE-scan\n";
+system("sh -c ./$MODULE-scan") == 0 or die "Scan failed: $!\n";
+
+unlink "./$MODULE-scan.c", "./$MODULE-scan.o", "./$MODULE-scan.lo", "./$MODULE-scan";
+
+#&UpdateFileIfChanged ($old_signals_filename, $new_signals_filename, 0);
+&UpdateFileIfChanged ($old_hierarchy_filename, $new_hierarchy_filename, 0);
+&UpdateFileIfChanged ($old_interfaces_filename, $new_interfaces_filename, 0);
+&UpdateFileIfChanged ($old_prerequisites_filename, $new_prerequisites_filename, 0);
+#&UpdateFileIfChanged ($old_args_filename, $new_args_filename, 0);
+
+
diff --git a/common/gtk-doc-plugins.mak b/common/gtk-doc-plugins.mak
new file mode 100644
index 0000000..f673a7f
--- /dev/null
+++ b/common/gtk-doc-plugins.mak
@@ -0,0 +1,384 @@
+# This is an include file specifically tuned for building documentation
+# for GStreamer plug-ins
+
+help:
+	@echo
+	@echo "If you are a doc maintainer, run 'make update' to update"
+	@echo "the documentation files maintained in CVS"
+	@echo
+	@echo Other useful make targets:
+	@echo
+	@echo  check-inspected-versions: make sure the inspected plugin info
+	@echo                            is up to date before a release
+	@echo
+
+# update the stuff maintained by doc maintainers
+update:
+	$(MAKE) inspect-update
+	$(MAKE) scanobj-update
+
+# We set GPATH here; this gives us semantics for GNU make
+# which are more like other make's VPATH, when it comes to
+# whether a source that is a target of one rule is then
+# searched for in VPATH/GPATH.
+#
+GPATH = $(srcdir)
+
+# thomas: make docs parallel installable
+TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)- at GST_MAJORMINOR@
+
+EXTRA_DIST = 				\
+	scanobj-build.stamp		\
+	$(srcdir)/inspect/*.xml		\
+	inspect.stamp			\
+	inspect-build.stamp		\
+	$(SCANOBJ_FILES)		\
+	$(content_files)		\
+	$(extra_files)			\
+	$(HTML_IMAGES)			\
+	$(DOC_MAIN_SGML_FILE)	\
+	$(DOC_OVERRIDES)		\
+	$(DOC_MODULE)-sections.txt
+
+MAINTAINER_DOC_STAMPS =			\
+	scanobj-build.stamp		\
+	inspect-build.stamp		\
+	inspect.stamp
+
+# we don't add inspect-build.stamp and scanobj-build.stamp here since they are
+# built manually by docs maintainers and result is commited to CVS
+DOC_STAMPS =				\
+	scan-build.stamp		\
+	tmpl-build.stamp		\
+	sgml-build.stamp		\
+	html-build.stamp		\
+	scan.stamp			\
+	tmpl.stamp			\
+	sgml.stamp			\
+	html.stamp
+
+# files generated/updated by gtkdoc-scangobj
+SCANOBJ_FILES =				\
+	$(DOC_MODULE).signals           \
+        $(DOC_MODULE).hierarchy         \
+        $(DOC_MODULE).interfaces        \
+        $(DOC_MODULE).prerequisites     \
+	$(DOC_MODULE).types		\
+        $(DOC_MODULE).args
+
+SCANOBJ_FILES_O =			\
+	.libs/$(DOC_MODULE)-scan.o
+
+# files generated/updated by gtkdoc-scan
+SCAN_FILES =				\
+	$(DOC_MODULE)-sections.txt	\
+	$(DOC_MODULE)-overrides.txt	\
+	$(DOC_MODULE)-undocumented.txt	\
+	$(DOC_MODULE)-decl.txt		\
+	$(DOC_MODULE)-decl-list.txt
+
+
+REPORT_FILES = \
+	$(DOC_MODULE)-undocumented.txt \
+	$(DOC_MODULE)-undeclared.txt \
+	$(DOC_MODULE)-unused.txt
+
+# FC3 seems to need -scan.c to be part of CLEANFILES for distcheck
+# no idea why FC4 can do without
+CLEANFILES = \
+	$(SCANOBJ_FILES_O) \
+	$(DOC_MODULE)-scan.c \
+	$(REPORT_FILES) \
+	$(DOC_STAMPS) \
+	inspect-registry.xml
+
+
+if ENABLE_GTK_DOC
+all-local: html-build.stamp
+
+#### scan gobjects; done by documentation maintainer ####
+scanobj-update:
+	-rm scanobj-build.stamp
+	$(MAKE) scanobj-build.stamp
+
+# in the case of non-srcdir builds, the built gst directory gets added
+# to gtk-doc scanning; but only then, to avoid duplicates
+# FIXME: since we don't have the scan step as part of the build anymore,
+# we could remove that
+# TODO: finish elite script that updates the output files of this step
+# instead of rewriting them, so that multiple maintainers can generate
+# a collective set of args and signals
+scanobj-build.stamp: $(SCANOBJ_DEPS) $(basefiles)
+	@echo '*** Scanning GObjects ***'
+	if test x"$(srcdir)" != x. ; then				\
+	    for f in $(SCANOBJ_FILES);					\
+	    do								\
+	        cp $(srcdir)/$$f . ;					\
+	    done;							\
+	else								\
+	    $(INSPECT_ENVIRONMENT) 					\
+	    CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)"				\
+	    CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)"				\
+	    LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)"				\
+	    $(GST_DOC_SCANOBJ) --type-init-func="gst_init(NULL,NULL)"	\
+	        --module=$(DOC_MODULE) --source=$(PACKAGE) &&		\
+		$(PYTHON)						\
+		$(top_srcdir)/common/scangobj-merge.py $(DOC_MODULE);	\
+	fi
+	touch scanobj-build.stamp
+
+$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(SCANOBJ_FILES_O): scan-build.stamp
+	@true
+
+### inspect GStreamer plug-ins; done by documentation maintainer ###
+
+# only look at the plugins in this module when building inspect .xml stuff
+INSPECT_REGISTRY=$(top_builddir)/docs/plugins/inspect-registry.xml
+INSPECT_ENVIRONMENT=\
+        GST_PLUGIN_SYSTEM_PATH= \
+        GST_PLUGIN_PATH=$(top_builddir)/gst:$(top_builddir)/sys:$(top_builddir)/ext:$(top_builddir)/plugins:$(top_builddir)/src:$(top_builddir)/gnl \
+        GST_REGISTRY=$(INSPECT_REGISTRY)
+
+# update the element and plugin XML descriptions; store in inspect/
+inspect:
+	mkdir inspect
+
+inspect-update: inspect
+	-rm -f $(INSPECT_REGISTRY) inspect-build.stamp
+	$(MAKE) inspect-build.stamp
+
+# FIXME: inspect.stamp should be written to by gst-xmlinspect.py
+# IF the output changed; see gtkdoc-mktmpl
+inspect-build.stamp:
+	@echo '*** Rebuilding plugin inspection files ***'
+	if test x"$(srcdir)" != x. ; then \
+	    cp $(srcdir)/inspect.stamp . ; \
+	    cp $(srcdir)/inspect-build.stamp . ; \
+	else \
+	    $(INSPECT_ENVIRONMENT) $(PYTHON) \
+	        $(top_srcdir)/common/gst-xmlinspect.py $(PACKAGE) inspect && \
+	    echo -n "timestamp" > inspect.stamp && \
+	    touch inspect-build.stamp; \
+        fi
+
+### scan headers; done on every build ###
+scan-build.stamp: $(HFILE_GLOB) $(EXTRA_HFILES) $(basefiles) scanobj-build.stamp inspect-build.stamp
+	if test "x$(top_srcdir)" != "x$(top_builddir)" &&		\
+	   test -d "$(top_builddir)/gst";				\
+        then								\
+            export BUILT_OPTIONS="--source-dir=$(top_builddir)/gst";	\
+        fi;								\
+	gtkdoc-scan							\
+	    $(SCAN_OPTIONS) $(EXTRA_HFILES)				\
+	    --module=$(DOC_MODULE)					\
+	    $$BUILT_OPTIONS						\
+	    --ignore-headers="$(IGNORE_HFILES)";			\
+	touch scan-build.stamp
+
+#### update templates; done on every build ####
+
+### FIXME: make this error out again when docs are fixed for 0.9
+# in a non-srcdir build, we need to copy files from the previous step
+# and the files from previous runs of this step
+tmpl-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_OVERRIDES)
+	@echo '*** Rebuilding template files ***'
+	if test x"$(srcdir)" != x. ; then				\
+	    for f in $(SCANOBJ_FILES) $(SCAN_FILES);			\
+	    do								\
+	        if test -e $(srcdir)/$$f; then cp $(srcdir)/$$f . ; fi; \
+	    done;							\
+	fi
+	gtkdoc-mktmpl --module=$(DOC_MODULE) | tee tmpl-build.log
+	$(PYTHON) \
+		$(top_srcdir)/common/mangle-tmpl.py $(srcdir)/inspect tmpl
+	@cat $(DOC_MODULE)-unused.txt
+	rm -f tmpl-build.log
+	touch tmpl-build.stamp
+
+tmpl.stamp: tmpl-build.stamp
+	@true
+
+#### build xml; done on every build ####
+
+### FIXME: make this error out again when docs are fixed for 0.9
+sgml-build.stamp: tmpl.stamp inspect.stamp $(CFILE_GLOB) $(top_srcdir)/common/plugins.xsl
+	@echo '*** Building XML ***'
+	@-mkdir -p xml
+	@for a in $(srcdir)/inspect/*.xml; do \
+            xsltproc --stringparam module $(MODULE) \
+		$(top_srcdir)/common/plugins.xsl $$a > xml/`basename $$a`; done
+	@for f in $(EXAMPLE_CFILES); do \
+		$(PYTHON) $(top_srcdir)/common/c-to-xml.py $$f > xml/element-`basename $$f .c`.xml; done
+	gtkdoc-mkdb \
+		--module=$(DOC_MODULE) \
+		--source-dir=$(DOC_SOURCE_DIR) \
+		--main-sgml-file=$(srcdir)/$(DOC_MAIN_SGML_FILE) \
+		--output-format=xml \
+		--ignore-files="$(IGNORE_HFILES) $(IGNORE_CFILES)" \
+		$(MKDB_OPTIONS) \
+		| tee sgml-build.log
+	@if grep "WARNING:" sgml-build.log > /dev/null; then true; fi # exit 1; fi
+	cp ../version.entities xml
+	rm sgml-build.log
+	touch sgml-build.stamp
+
+sgml.stamp: sgml-build.stamp
+	@true
+
+#### build html; done on every step ####
+
+html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files)
+	@echo '*** Building HTML ***'
+	if test -d html; then rm -rf html; fi
+	mkdir html
+	cp $(srcdir)/$(DOC_MAIN_SGML_FILE) html
+	@for f in $(content_files); do cp $(srcdir)/$$f html; done
+	cp -pr xml html
+	cp ../version.entities html
+	cd html && gtkdoc-mkhtml $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) \
+	    2>&1 | tee ../html-build.log
+	@if grep "warning:" html-build.log > /dev/null; then \
+		echo "ERROR"; grep "warning:" html-build.log; exit 1; fi
+	@rm html-build.log
+	mv html/index.sgml html/index.sgml.bak
+	$(SED) "s/ href=\"$(DOC_MODULE)\// href=\"$(DOC_MODULE)- at GST_MAJORMINOR@\//g" html/index.sgml.bak >html/index.sgml
+	rm -f html/index.sgml.bak
+	rm -f html/$(DOC_MAIN_SGML_FILE)
+	rm -rf html/xml
+	rm -f html/version.entities
+	test "x$(HTML_IMAGES)" = "x" || for i in "" $(HTML_IMAGES) ; do \
+	    if test "$$i" != ""; then cp $(srcdir)/$$i html ; fi; done
+	@echo '-- Fixing Crossreferences' 
+	gtkdoc-fixxref --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS)
+	touch html-build.stamp
+
+clean-local-gtkdoc:
+	rm -rf xml tmpl html
+# clean files copied for nonsrcdir templates build
+	if test x"$(srcdir)" != x. ; then \
+	    rm -rf $(SCANOBJ_FILES) $(SCAN_FILES); \
+	fi
+else
+all-local:
+clean-local-gtkdoc:
+endif
+
+clean-local: clean-local-gtkdoc
+	rm -f *~ *.bak
+	rm -rf .libs
+
+distclean-local: clean
+	rm -rf tmpl/*.sgml.bak
+	rm -rf *.o
+
+MAINTAINERCLEANFILES = $(MAINTAINER_DOC_STAMPS)
+
+# thomas: make docs parallel installable; devhelp requires majorminor too
+install-data-local:
+	(installfiles=`echo $(srcdir)/html/*.sgml $(srcdir)/html/*.html $(srcdir)/html/*.png $(srcdir)/html/*.css`; \
+	if test "$$installfiles" = '$(srcdir)/html/*.sgml $(srcdir)/html/*.html $(srcdir)/html/*.png $(srcdir)/html/*.css'; \
+	then echo '-- Nothing to install' ; \
+	else \
+          $(mkinstalldirs) $(DESTDIR)$(TARGET_DIR); \
+	  for i in $$installfiles; do \
+	    echo '-- Installing '$$i ; \
+	    $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \
+	  done; \
+	  pngfiles=`echo ./html/*.png`; \
+	  if test "$$pngfiles" != './html/*.png'; then \
+	    for i in $$pngfiles; do \
+	      echo '-- Installing '$$i ; \
+	      $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \
+	    done; \
+	  fi; \
+	  echo '-- Installing $(srcdir)/html/$(DOC_MODULE).devhelp' ; \
+	  $(INSTALL_DATA) $(srcdir)/html/$(DOC_MODULE).devhelp \
+	    $(DESTDIR)$(TARGET_DIR)/$(DOC_MODULE)- at GST_MAJORMINOR@.devhelp; \
+	  if test -e $(srcdir)/html/$(DOC_MODULE).devhelp2; then \
+        	    $(INSTALL_DATA) $(srcdir)/html/$(DOC_MODULE).devhelp2 \
+	           $(DESTDIR)$(TARGET_DIR)/$(DOC_MODULE)- at GST_MAJORMINOR@.devhelp2; \
+	  fi; \
+	  (which gtkdoc-rebase >/dev/null && \
+	    gtkdoc-rebase --relative --dest-dir=$(DESTDIR) --html-dir=$(DESTDIR)$(TARGET_DIR)) || true ; \
+	fi) 
+uninstall-local:
+	(installfiles=`echo ./html/*.html`; \
+	if test "$$installfiles" = './html/*.html'; \
+	then echo '-- Nothing to uninstall' ; \
+	else \
+	  for i in $$installfiles; do \
+	    rmfile=`basename $$i` ; \
+	    echo '-- Uninstalling $(DESTDIR)$(TARGET_DIR)/'$$rmfile ; \
+	    rm -f $(DESTDIR)$(TARGET_DIR)/$$rmfile; \
+	  done; \
+	  pngfiles=`echo ./html/*.png`; \
+	  if test "$$pngfiles" != './html/*.png'; then \
+	    for i in $$pngfiles; do \
+	      rmfile=`basename $$i` ; \
+	      echo '-- Uninstalling $(DESTDIR)$(TARGET_DIR)/'$$rmfile ; \
+	      rm -f $(DESTDIR)$(TARGET_DIR)/$$rmfile; \
+	    done; \
+	  fi; \
+	  echo '-- Uninstalling $(DESTDIR)$(TARGET_DIR)/$(DOC_MODULE).devhelp' ; \
+	  rm -f $(DESTDIR)$(TARGET_DIR)/$(DOC_MODULE)- at GST_MAJORMINOR@.devhelp; \
+	  if test -e $(DESTDIR)$(TARGET_DIR)/$(DOC_MODULE)- at GST_MAJORMINOR@.devhelp2; then \
+	    rm -f $(DESTDIR)$(TARGET_DIR)/$(DOC_MODULE)- at GST_MAJORMINOR@.devhelp2; \
+	  fi; \
+	  echo '-- Uninstalling $(DESTDIR)$(TARGET_DIR)/index.sgml' ; \
+	  rm -f $(DESTDIR)$(TARGET_DIR)/index.sgml; \
+		if test -e $(DESTDIR)$(TARGET_DIR)/style.css; then \
+			echo '-- Uninstalling $(DESTDIR)$(TARGET_DIR)/style.css' ; \
+			rm -f $(DESTDIR)$(TARGET_DIR)/style.css; \
+		fi; \
+	fi) 
+	if test -d $(DESTDIR)$(TARGET_DIR); then rmdir -p --ignore-fail-on-non-empty $(DESTDIR)$(TARGET_DIR) 2>/dev/null; fi; true
+
+#
+# Checks
+#
+check-hierarchy: $(DOC_MODULE).hierarchy
+	@if grep '	' $(DOC_MODULE).hierarchy; then \
+	    echo "$(DOC_MODULE).hierarchy contains tabs, please fix"; \
+	    /bin/false; \
+	fi
+
+check: check-hierarchy
+
+# wildcard is apparently not portable to other makes, hence the use of find
+inspect_files = $(shell find $(srcdir)/inspect -name '*.xml')
+
+check-inspected-versions:
+	@echo Checking plugin versions of inspected plugin data ...; \
+	fail=0 ; \
+	for each in $(inspect_files) ; do \
+	  if (grep -H '<version>' $$each | grep -v '<version>$(VERSION)'); then \
+	    echo $$each should be fixed to say version $(VERSION) or be removed ; \
+	    echo "sed -i -e 's/<version.*version>/<version>$(VERSION)<\/version>/'" $$each; \
+	    echo ; \
+	    fail=1; \
+	  fi ; \
+	done ; \
+	exit $$fail
+
+#
+# Require gtk-doc when making dist
+#
+if ENABLE_GTK_DOC
+dist-check-gtkdoc:
+else
+dist-check-gtkdoc:
+	@echo "*** gtk-doc must be installed and enabled in order to make dist"
+	@false
+endif
+
+# FIXME: decide whether we want to dist generated html or not
+dist-hook: dist-check-gtkdoc dist-hook-local
+	mkdir $(distdir)/html
+	cp $(srcdir)/html/* $(distdir)/html
+	-cp $(srcdir)/$(DOC_MODULE).types $(distdir)/
+	-cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/
+	cd $(distdir) && rm -f $(DISTCLEANFILES)
+        -gtkdoc-rebase --online --relative --html-dir=$(distdir)/html
+
+.PHONY : dist-hook-local docs
+
diff --git a/common/mangle-tmpl.py b/common/mangle-tmpl.py
new file mode 100644
index 0000000..6cb52d7
--- /dev/null
+++ b/common/mangle-tmpl.py
@@ -0,0 +1,158 @@
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+"""
+use the output from gst-xmlinspect.py to mangle tmpl/*.sgml and
+insert/overwrite Short Description and Long Description
+"""
+
+# FIXME: right now it uses pygst and scans on its own;
+# we really should use inspect/*.xml instead since the result of
+# gst-xmlinspect.py is commited by the docs maintainer, who can be
+# expected to have pygst, but this step should be done for every docs build,
+# so no pygst allowed
+
+# read in inspect/*.xml
+# for every tmpl/element-(name).xml: mangle with details from element
+
+import glob
+import re
+import sys
+import os
+
+class Tmpl:
+    def __init__(self, filename):
+        self.filename = filename
+        self._sectionids = []
+        self._sections = {}
+
+    def read(self):
+        """
+        Read and parse the sections from the given file.
+        """
+        lines = open(self.filename).readlines()
+        matcher = re.compile("<!-- ##### SECTION (\S+) ##### -->\n")
+        id = None
+
+        for line in lines:
+            match = matcher.search(line)
+            if match:
+                id = match.expand("\\1")
+                self._sectionids.append(id)
+                self._sections[id] = []
+            else:
+                if not id:
+                    sys.stderr.write(
+                        "WARNING: line before a SECTION header: %s" % line)
+                else:
+                    self._sections[id].append(line)
+
+    def get_section(self, id):
+        """
+        Get the content from the given section.
+        """
+        return self._sections[id]
+
+    def set_section(self, id, content):
+        """
+        Replace the given section id with the given content.
+        """
+        self._sections[id] = content
+
+    def output(self):
+        """
+        Return the output of the current template in the tmpl/*.sgml format.
+        """
+        lines = []
+        for id in self._sectionids:
+            lines.append("<!-- ##### SECTION %s ##### -->\n" % id)
+            for line in self._sections[id]:
+                lines.append(line)
+
+        return "".join(lines)
+
+    def write(self, backup=False):
+        """
+        Write out the template file again, backing up the previous one.
+        """
+        if backup:
+            target = self.filename + ".mangle.bak"
+            os.rename(self.filename, target)
+
+        handle = open(self.filename, "w")
+        handle.write(self.output())
+        handle.close()
+
+import xml.dom.minidom
+
+def get_elements(file):
+    elements = {}
+    doc = xml.dom.minidom.parse(file)
+
+    elem = None
+    for e in doc.childNodes:
+        if e.nodeType == e.ELEMENT_NODE and e.localName == 'plugin':
+            elem = e
+            break
+    if elem == None:
+        return None
+
+    elem2 = None
+    for e in elem.childNodes:
+        if e.nodeType == e.ELEMENT_NODE and e.localName == 'elements':
+            elem2 = e
+            break
+    if elem2 == None:
+        return None
+
+    elem = elem2
+
+    for e in elem.childNodes:
+        if e.nodeType == e.ELEMENT_NODE and e.localName == 'element':
+            name = None
+            description = None
+
+            for e2 in e.childNodes:
+                if e2.nodeType == e2.ELEMENT_NODE and e2.localName == 'name':
+                    name = e2.childNodes[0].nodeValue.encode("UTF-8")
+                elif e2.nodeType == e2.ELEMENT_NODE and e2.localName == 'description':
+                    description = e2.childNodes[0].nodeValue.encode("UTF-8")
+
+            if name != None and description != None:
+                elements[name] = {'description': description}
+
+    return elements
+        
+def main():
+    if not len(sys.argv) == 3:
+        sys.stderr.write('Please specify the inspect/ dir and the tmpl/ dir')
+        sys.exit(1)
+
+    inspectdir = sys.argv[1]
+    tmpldir = sys.argv[2]
+
+    # parse all .xml files; build map of element name -> short desc
+    #for file in glob.glob("inspect/plugin-*.xml"):
+    elements = {}
+    for file in glob.glob("%s/plugin-*.xml" % inspectdir):
+        elements.update(get_elements(file))
+
+    for file in glob.glob("%s/element-*.sgml" % tmpldir):
+        base = os.path.basename(file)
+        element = base[len("element-"):-len(".sgml")]
+        tmpl = Tmpl(file)
+        tmpl.read()
+        if element in elements.keys():
+            description = elements[element]['description']
+            tmpl.set_section("Short_Description", "%s\n\n" % description)
+
+        # put in an include if not yet there
+        line = '<include xmlns="http://www.w3.org/2003/XInclude" href="' + \
+            'element-' + element + '-details.xml" />\n'
+        section = tmpl.get_section("Long_Description")
+        if not section[0]  == line:
+            section.insert(0, line)
+        tmpl.set_section("Long_Description", section)
+        tmpl.write()
+
+main()
diff --git a/common/plugins.xsl b/common/plugins.xsl
new file mode 100644
index 0000000..2eab220
--- /dev/null
+++ b/common/plugins.xsl
@@ -0,0 +1,209 @@
+<?xml version='1.0'?> <!--*- mode: xml -*-->
+
+<xsl:stylesheet
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:exsl="http://exslt.org/common"
+  xmlns:str="http://exslt.org/strings"
+  extension-element-prefixes="exsl str"
+  version="1.0">
+<xsl:output method="xml" indent="yes"
+            doctype-public ="-//OASIS//DTD DocBook XML V4.1.2//EN"
+            doctype-system = "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"/> 
+
+<xsl:param name="module" />
+
+  <xsl:template match="element">
+    <xsl:element name="varlistentry">
+      <xsl:element name="term">
+        <xsl:element name="link">
+          <xsl:attribute name="linkend"><xsl:value-of select="$module" />-plugins-<xsl:value-of select="name"/></xsl:attribute>
+          <xsl:value-of select="name" />
+        </xsl:element>
+      </xsl:element>
+      <xsl:element name="listitem">
+        <xsl:element name="simpara"><xsl:value-of select="description" /></xsl:element>
+      </xsl:element>
+    </xsl:element>
+    <xsl:variable name="name"><xsl:copy-of select="name"/></xsl:variable>
+    <!-- here we write an element-(name)-details.xml file for the element -->
+    <exsl:document href="{concat ('xml/element-', $name, '-details.xml')}" method="xml" indent="yes">
+
+      <xsl:element name="refsynopsisdiv">
+        <xsl:element name="refsect2">
+          <xsl:element name="title">Element Information</xsl:element>
+          <xsl:element name="variablelist">
+          
+            <!-- plugin name and link -->
+            <xsl:element name="varlistentry">
+              <xsl:element name="term">plugin</xsl:element>
+              <xsl:element name="listitem">
+                <xsl:element name="simpara">
+                  <xsl:element name="link">
+                    <xsl:attribute name="linkend">plugin-<xsl:value-of select="../../name"/></xsl:attribute>
+                    <xsl:value-of select="../../name" />
+                  </xsl:element>
+                </xsl:element>
+              </xsl:element>
+            </xsl:element>
+          
+            <xsl:element name="varlistentry">
+              <xsl:element name="term">author</xsl:element>
+              <xsl:element name="listitem">
+                <xsl:element name="simpara"><xsl:value-of select="author" /></xsl:element>
+              </xsl:element>
+            </xsl:element>
+          
+            <xsl:element name="varlistentry">
+              <xsl:element name="term">class</xsl:element>
+              <xsl:element name="listitem">
+                <xsl:element name="simpara"><xsl:value-of select="class" /></xsl:element>
+              </xsl:element>
+            </xsl:element>
+                        
+          </xsl:element> <!-- variablelist -->
+        </xsl:element> <!-- refsect2 -->
+  
+        <xsl:element name="refsect2">
+          <xsl:element name="title">Element Pads</xsl:element>
+          <!-- process all caps -->
+          <xsl:for-each select="pads/caps">
+            <xsl:element name="variablelist">
+              <xsl:element name="varlistentry">
+                <xsl:element name="term">name</xsl:element>
+                <xsl:element name="listitem">
+                  <xsl:element name="simpara"><xsl:value-of select="name" /></xsl:element>
+                </xsl:element>
+              </xsl:element>
+              
+              <xsl:element name="varlistentry">
+                <xsl:element name="term">direction</xsl:element>
+                <xsl:element name="listitem">
+                  <xsl:element name="simpara"><xsl:value-of select="direction" /></xsl:element>
+                </xsl:element>
+              </xsl:element>
+              
+              <xsl:element name="varlistentry">
+                <xsl:element name="term">presence</xsl:element>
+                <xsl:element name="listitem">
+                  <xsl:element name="simpara"><xsl:value-of select="presence" /></xsl:element>
+                </xsl:element>
+              </xsl:element>
+              
+              <xsl:for-each select='str:tokenize(details, ";")'>
+                <xsl:element name="varlistentry">
+                  <xsl:element name="term">
+                    <xsl:if test="position()=1">details</xsl:if>
+                  </xsl:element>
+                  <xsl:element name="listitem">
+                    <xsl:element name="simpara"><xsl:value-of select='.'/></xsl:element>
+                  </xsl:element>
+                </xsl:element>
+              </xsl:for-each>
+  
+            </xsl:element> <!-- variablelist -->
+  
+            <!--xsl:element name="programlisting"><xsl:value-of select="details" /></xsl:element-->
+  
+          </xsl:for-each>
+        </xsl:element> <!-- refsect2 -->
+      </xsl:element> <!-- refsynopsisdiv -->
+
+    </exsl:document>
+  </xsl:template>
+
+  <xsl:template match="plugin">
+    <xsl:element name="refentry">
+      <xsl:attribute name="id"><xsl:value-of select="$module" />-plugins-plugin-<xsl:value-of select="name"/></xsl:attribute>
+
+      <xsl:element name="refmeta">
+        <xsl:element name="refentrytitle">
+          <xsl:value-of select="name"/>
+        </xsl:element>
+        <xsl:element name="manvolnum">3</xsl:element>
+        <xsl:element name="refmiscinfo">FIXME Library</xsl:element>
+      </xsl:element> <!-- refmeta -->
+
+      <xsl:element name="refnamediv">
+        <xsl:element name="refname">
+          <xsl:element name="anchor">
+            <xsl:attribute name="id">plugin-<xsl:value-of select="name"/></xsl:attribute>
+            <xsl:value-of select="name"/>
+          </xsl:element>
+        </xsl:element>
+  
+        <xsl:element name="refpurpose">
+          <xsl:value-of select="description"/>
+        </xsl:element>
+      </xsl:element>
+
+      <xsl:element name="refsect1">
+        <xsl:element name="title">Plugin Information</xsl:element>
+        <xsl:element name="variablelist">
+
+          <xsl:element name="varlistentry">
+            <xsl:element name="term">filename</xsl:element>
+            <xsl:element name="listitem">
+              <xsl:element name="simpara"><xsl:value-of select="basename" /></xsl:element>
+            </xsl:element>
+          </xsl:element>
+
+          <xsl:element name="varlistentry">
+            <xsl:element name="term">version</xsl:element>
+            <xsl:element name="listitem">
+              <xsl:element name="simpara"><xsl:value-of select="version" /></xsl:element>
+            </xsl:element>
+          </xsl:element>
+
+          <xsl:element name="varlistentry">
+            <xsl:element name="term">run-time license</xsl:element>
+            <xsl:element name="listitem">
+              <xsl:element name="simpara"><xsl:value-of select="license" /></xsl:element>
+            </xsl:element>
+          </xsl:element>
+
+          <xsl:element name="varlistentry">
+            <xsl:element name="term">package</xsl:element>
+            <xsl:element name="listitem">
+              <xsl:element name="simpara"><xsl:value-of select="package" /></xsl:element>
+            </xsl:element>
+          </xsl:element>
+
+          <xsl:element name="varlistentry">
+            <xsl:element name="term">origin</xsl:element>
+            <xsl:element name="listitem">
+              <xsl:element name="simpara">
+                <!-- only show origin as link if it starts with http -->
+                <xsl:choose>
+                  <xsl:when test="substring(@href, 1, 4) = 'http'">
+                    <xsl:element name="ulink">
+                      <xsl:attribute name="url"><xsl:value-of select="origin" /></xsl:attribute>
+                      <xsl:value-of select="origin" />
+                    </xsl:element>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <xsl:value-of select="origin" />
+                  </xsl:otherwise>
+                </xsl:choose>
+              </xsl:element>
+            </xsl:element>
+          </xsl:element>
+
+        </xsl:element>
+      </xsl:element>
+
+      <xsl:element name="refsect1">
+        <xsl:element name="title">Elements</xsl:element>
+        <!-- process all elements -->
+        <xsl:element name="variablelist">
+          <xsl:apply-templates select="elements"/>
+        </xsl:element>
+      </xsl:element>
+
+    </xsl:element>
+
+  </xsl:template>
+
+  <!-- ignore -->
+  <xsl:template match="gst-plugin-paths" />
+
+</xsl:stylesheet>
diff --git a/common/scangobj-merge.py b/common/scangobj-merge.py
new file mode 100755
index 0000000..a79b404
--- /dev/null
+++ b/common/scangobj-merge.py
@@ -0,0 +1,278 @@
+#!/usr/bin/python
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+"""
+parse, update and write .signals and .args files
+"""
+
+import sys
+import os
+
+def debug(*args):
+    pass
+
+# OrderedDict class based on
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
+# Licensed under the Python License
+class OrderedDict(dict):
+    def __init__(self, dict = None):
+        self._keys = []
+        dict.__init__(self, dict)
+
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        self._keys.remove(key)
+
+    def __setitem__(self, key, item):
+        dict.__setitem__(self, key, item)
+        if key not in self._keys: self._keys.append(key)
+
+    def clear(self):
+        dict.clear(self)
+        self._keys = []
+
+    def copy(self):
+        dict = dict.copy(self)
+        dict._keys = self._keys[:]
+        return dict
+
+    def items(self):
+        return zip(self._keys, self.values())
+
+    def keys(self):
+        return self._keys
+
+    def popitem(self):
+        try:
+            key = self._keys[-1]
+        except IndexError:
+            raise KeyError('dictionary is empty')
+
+        val = self[key]
+        del self[key]
+
+        return (key, val)
+
+    def setdefault(self, key, failobj = None):
+        dict.setdefault(self, key, failobj)
+        if key not in self._keys: self._keys.append(key)
+
+    def update(self, dict):
+        dict.update(self, dict)
+        for key in dict.keys():
+            if key not in self._keys: self._keys.append(key)
+
+    def values(self):
+        return map(self.get, self._keys)
+
+class Object:
+    def __init__(self, name):
+        self._signals = OrderedDict()
+        self._args = OrderedDict()
+        self.name = name
+
+    def __repr__(self):
+        return "<Object %s>" % self.name
+
+    def add_signal(self, signal, overwrite=True):
+        if not overwrite and self._signals.has_key(signal.name):
+            raise IndexError, "signal %s already in %r" % (signal.name, self)
+        self._signals[signal.name] = signal
+
+    def add_arg(self, arg, overwrite=True):
+        if not overwrite and self._args.has_key(arg.name):
+            raise IndexError, "arg %s already in %r" % (arg.name, self)
+        self._args[arg.name] = arg
+        
+class Docable:
+    def __init__(self, **kwargs):
+        for key in self.attrs:
+            setattr(self, key, kwargs[key])
+        self.dict = kwargs
+
+    def __repr__(self):
+        return "<%r %s>" % (str(self.__class__), self.name)
+
+class Signal(Docable):
+    attrs = ['name', 'returns', 'args']
+
+class Arg(Docable):
+    attrs = ['name', 'type', 'range', 'flags', 'nick', 'blurb', 'default']
+
+class GDoc:
+    def load_file(self, filename):
+        try:
+            lines = open(filename).readlines()
+            self.load_data("".join(lines))
+        except IOError:
+            print "WARNING - could not read from %s" % filename
+
+    def save_file(self, filename, backup=False):
+        """
+        Save the signals information to the given .signals file if the
+        file content changed.
+        """
+        olddata = None
+        try:
+            lines = open(filename).readlines()
+            olddata = "".join(lines)
+        except IOError:
+            print "WARNING - could not read from %s" % filename
+        newdata = self.get_data()
+        if olddata and olddata == newdata:
+            return
+
+        if olddata:
+            if backup:
+                os.rename(filename, filename + '.bak')
+
+        handle = open(filename, "w")
+        handle.write(newdata)
+        handle.close()
+
+class Signals(GDoc):
+    def __init__(self):
+        self._objects = OrderedDict()
+
+    def load_data(self, data):
+        """
+        Load the .signals lines, creating our list of objects and signals.
+        """
+        import re
+        smatcher = re.compile(
+            '(?s)'                                      # make . match \n
+            '<SIGNAL>\n(.*?)</SIGNAL>\n'
+            )
+        nmatcher = re.compile(
+            '<NAME>'
+            '(?P<object>\S*)'                           # store object
+            '::'
+            '(?P<signal>\S*)'                           # store signal
+            '</NAME>'
+        )
+        rmatcher = re.compile(
+            '(?s)'                                      # make . match \n
+            '<RETURNS>(?P<returns>\S*)</RETURNS>\n'     # store returns
+            '(?P<args>.*)'                              # store args
+        )
+        for block in smatcher.findall(data):
+            nmatch = nmatcher.search(block)
+            if nmatch:
+                o = nmatch.group('object')
+                debug("Found object", o)
+                debug("Found signal", nmatch.group('signal'))
+                if not self._objects.has_key(o):
+                    object = Object(o)
+                    self._objects[o] = object
+
+                rmatch = rmatcher.search(block)
+                if rmatch:
+                    dict = rmatch.groupdict().copy()
+                    dict['name'] = nmatch.group('signal')
+                    signal = Signal(**dict)
+                    self._objects[o].add_signal(signal)
+
+    def get_data(self):
+        lines = []
+        for o in self._objects.values():
+            for s in o._signals.values():
+                block = """<SIGNAL>
+<NAME>%(object)s::%(name)s</NAME>
+<RETURNS>%(returns)s</RETURNS>
+%(args)s</SIGNAL>
+"""
+                d = s.dict.copy()
+                d['object'] = o.name
+                lines.append(block % d)
+
+        return "\n".join(lines) + '\n'
+
+class Args(GDoc):
+    def __init__(self):
+        self._objects = OrderedDict()
+
+    def load_data(self, data):
+        """
+        Load the .args lines, creating our list of objects and args.
+        """
+        import re
+        amatcher = re.compile(
+            '(?s)'                                      # make . match \n
+            '<ARG>\n(.*?)</ARG>\n'
+            )
+        nmatcher = re.compile(
+            '<NAME>'
+            '(?P<object>\S*)'                           # store object
+            '::'
+            '(?P<arg>\S*)'                              # store arg
+            '</NAME>'
+        )
+        rmatcher = re.compile(
+            '(?s)'                                      # make . match \n
+            '<TYPE>(?P<type>\S*)</TYPE>\n'              # store type
+            '<RANGE>(?P<range>.*?)</RANGE>\n'           # store range
+            '<FLAGS>(?P<flags>\S*)</FLAGS>\n'           # store flags
+            '<NICK>(?P<nick>.*?)</NICK>\n'              # store nick
+            '<BLURB>(?P<blurb>.*?)</BLURB>\n'           # store blurb
+            '<DEFAULT>(?P<default>.*?)</DEFAULT>\n'     # store default
+        )
+        for block in amatcher.findall(data):
+            nmatch = nmatcher.search(block)
+            if nmatch:
+                o = nmatch.group('object')
+                debug("Found object", o)
+                debug("Found arg", nmatch.group('arg'))
+                if not self._objects.has_key(o):
+                    object = Object(o)
+                    self._objects[o] = object
+
+                rmatch = rmatcher.search(block)
+                if rmatch:
+                    dict = rmatch.groupdict().copy()
+                    dict['name'] = nmatch.group('arg')
+                    arg = Arg(**dict)
+                    self._objects[o].add_arg(arg)
+                else:
+                    print "ERROR: could not match arg from block %s" % block
+
+    def get_data(self):
+        lines = []
+        for o in self._objects.values():
+            for a in o._args.values():
+                block = """<ARG>
+<NAME>%(object)s::%(name)s</NAME>
+<TYPE>%(type)s</TYPE>
+<RANGE>%(range)s</RANGE>
+<FLAGS>%(flags)s</FLAGS>
+<NICK>%(nick)s</NICK>
+<BLURB>%(blurb)s</BLURB>
+<DEFAULT>%(default)s</DEFAULT>
+</ARG>
+"""
+                d = a.dict.copy()
+                d['object'] = o.name
+                lines.append(block % d)
+
+        return "\n".join(lines) + '\n'
+
+def main(argv):
+    modulename = None
+    try:
+        modulename = argv[1]
+    except IndexError:
+        sys.stderr.write('Pleae provide a documentation module name\n')
+        sys.exit(1)
+
+    print "Merging scangobj output for %s" % modulename
+    signals = Signals()
+    signals.load_file(modulename + '.signals')
+    signals.load_file(modulename + '.signals.new')
+    signals.save_file(modulename + '.signals', backup=True)
+
+    args = Args()
+    args.load_file(modulename + '.args')
+    args.load_file(modulename + '.args.new')
+    args.save_file(modulename + '.args', backup=True)
+
+main(sys.argv)
diff --git a/configure.ac b/configure.ac
index fee1a63..f13978f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -391,6 +391,7 @@ tests/gui/Makefile
 tests/commandline/Makefile
 docs/Makefile
 docs/libs/Makefile
+docs/plugins/Makefile
 docs/version.entities
 python/Makefile
 dnl docs/plugins/Makefile
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 262eebd..2f2b703 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -1,6 +1,4 @@
-PLUGIN_DOCS_DIRS =
 
-SUBDIRS = libs $(PLUGIN_DOCS_DIRS)
-DIST_SUBDIRS = libs
+SUBDIRS = libs plugins
 
 EXTRA_DIST = codingstyle.txt
diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am
new file mode 100644
index 0000000..d0e7adc
--- /dev/null
+++ b/docs/plugins/Makefile.am
@@ -0,0 +1,98 @@
+GST_DOC_SCANOBJ = $(top_srcdir)/common/gstdoc-scangobj
+
+## Process this file with automake to produce Makefile.in
+
+# The name of the module, e.g. 'glib'.
+#DOC_MODULE=gst-plugins-libs- at GST_MAJORMINOR@
+MODULE=farsight2
+DOC_MODULE=$(MODULE)-plugins
+
+# generated basefiles
+#basefiles = \
+##		$(DOC_MODULE).types \
+#		$(DOC_MODULE)-sections.txt \
+#		$(DOC_MODULE)-docs.sgml
+
+# ugly hack to make -unused.sgml work
+#unused-build.stamp:
+#	BUILDDIR=`pwd` && \
+#	cd $(srcdir)/tmpl && \
+#	ln -sf gstreamer-libs-unused.sgml \
+#		$$BUILDDIR/tmpl/gstreamer-libs- at GST_MAJORMINOR@-unused.sgml
+#	touch unused-build.stamp
+
+# these rules are added to create parallel docs using GST_MAJORMINOR
+#$(basefiles): gstreamer-libs- at GST_MAJORMINOR@%: gstreamer-libs%
+#	cp $< $@
+
+#CLEANFILES = $(basefiles)
+
+# The top-level SGML file. Change it if you want.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
+
+# The directory containing the source code. Relative to $(top_srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting functions and macros.
+DOC_SOURCE_DIR = $(top_srcdir)
+
+# Extra options to supply to gtkdoc-scan.
+SCAN_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkdb.
+MKDB_OPTIONS=--sgml-mode
+
+# Extra options to supply to gtkdoc-fixref.
+FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \
+	--extra-dir=$(GST_PREFIX)/share/gtk-doc/html \
+        	--extra-dir=$(datadir)/gtk-doc/html
+
+# Used for dependencies.
+HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h
+CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.c
+
+# this is a wingo addition
+# thomasvs: another nice wingo addition would be an explanation on why
+# this is useful ;)
+
+SCANOBJ_DEPS =
+
+# Header files to ignore when scanning.
+IGNORE_HFILES = 
+IGNORE_CFILES = 
+
+# we add all .h files of elements that have signals/args we want
+# sadly this also pulls in the private methods - maybe we should
+# move those around in the source ?
+# also, we should add some stuff here conditionally based on whether
+# or not the plugin will actually build
+# but I'm not sure about that - it might be this Just Works given that
+# the registry won't have the element
+
+EXTRA_HFILES = \
+	$(top_srcdir)/gst/funnel/fs-funnel.h \
+	$(top_srcdir)/gst/videoanyrate/videoanyrate.h \
+	$(top_srcdir)/gst/fsrtpconference/fs-rtp-conference.h
+
+# Images to copy into HTML directory.
+HTML_IMAGES =
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+content_files =
+
+# Other files to distribute.
+extra_files =
+
+# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib
+# contains GtkObjects/GObjects and you want to document signals and properties.
+GTKDOC_CFLAGS = $(GST_BASE_CFLAGS) -I$(top_builddir) -I$(top_builddir)/gst-libs
+GTKDOC_LIBS = $(SCANOBJ_DEPS) $(GST_BASE_LIBS)
+
+GTKDOC_CC=$(LIBTOOL) --mode=compile $(CC)
+GTKDOC_LD=$(LIBTOOL) --mode=link $(CC)
+
+# If you need to override some of the declarations, place them in this file
+# and uncomment this line.
+#DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt
+DOC_OVERRIDES =
+
+include $(top_srcdir)/common/gtk-doc-plugins.mak
diff --git a/docs/plugins/farsight2-plugins-docs.sgml b/docs/plugins/farsight2-plugins-docs.sgml
new file mode 100644
index 0000000..588a406
--- /dev/null
+++ b/docs/plugins/farsight2-plugins-docs.sgml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+  <bookinfo>
+    <title>farsight2-plugins Reference Manual</title>
+    <releaseinfo>
+      for farsight2-plugins [VERSION]
+      The latest version of this documentation can be found on-line at
+      <ulink role="online-location" url="http://[SERVER]/farsight2-plugins/index.html">http://[SERVER]/farsight2-plugins/</ulink>.
+    </releaseinfo>
+  </bookinfo>
+
+  <chapter>
+    <title>[Insert title here]</title>
+    <xi:include href="xml/fs-funnel.xml"/>
+    <xi:include href="xml/videoanyrate.xml"/>
+    <xi:include href="xml/fs-rtp-conference.xml"/>
+  </chapter>
+</book>
diff --git a/docs/plugins/farsight2-plugins-sections.txt b/docs/plugins/farsight2-plugins-sections.txt
new file mode 100644
index 0000000..e199859
--- /dev/null
+++ b/docs/plugins/farsight2-plugins-sections.txt
@@ -0,0 +1,47 @@
+<SECTION>
+<FILE>fs-funnel</FILE>
+<TITLE>FsFunnel</TITLE>
+FsFunnel
+FsFunnelClass
+<SUBSECTION Standard>
+FS_FUNNEL
+FS_IS_FUNNEL
+FS_TYPE_FUNNEL
+fs_funnel_get_type
+FS_FUNNEL_CLASS
+FS_IS_FUNNEL_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>videoanyrate</FILE>
+GstVideoanyratePrivate
+FsRtpConferenceClass
+<TITLE>GstVideoanyrate</TITLE>
+GstVideoanyrate
+<SUBSECTION Standard>
+GST_VIDEOANYRATE
+GST_IS_VIDEOANYRATE
+GST_TYPE_VIDEOANYRATE
+gst_videoanyrate_get_type
+GST_VIDEOANYRATE_CLASS
+GST_IS_VIDEOANYRATE_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>fs-rtp-conference</FILE>
+FS_RTP_CONFERENCE_CAST
+FsRtpConferencePrivate
+GstVideoanyrateClass
+<TITLE>FsRtpConference</TITLE>
+FsRtpConference
+<SUBSECTION Standard>
+fs_codec_to_gst_caps
+FS_RTP_CONFERENCE
+FS_IS_RTP_CONFERENCE
+FS_TYPE_RTP_CONFERENCE
+fs_rtp_conference_get_type
+FS_RTP_CONFERENCE_CLASS
+FS_IS_RTP_CONFERENCE_CLASS
+FS_RTP_CONFERENCE_GET_CLASS
+</SECTION>
+
diff --git a/docs/plugins/farsight2-plugins.types b/docs/plugins/farsight2-plugins.types
new file mode 100644
index 0000000..9f4950e
--- /dev/null
+++ b/docs/plugins/farsight2-plugins.types
@@ -0,0 +1 @@
+#include <gst/gst.h>
-- 
1.5.6.5




More information about the farsight-commits mailing list