=== modified file 'lib/group.c'
--- lib/group.c	2008-04-08 21:33:09 +0000
+++ lib/group.c	2008-12-03 14:42:45 +0000
@@ -35,7 +35,7 @@
 
   GList *objects;
 
-  PropDescription *pdesc;
+  const PropDescription *pdesc;
 };
 
 typedef struct _GroupPropChange GroupPropChange;
@@ -406,19 +406,10 @@
 {
   int i;
   if (group->pdesc == NULL) {
-    GList *descs = NULL, *tmp;
-
-    /* create list of property descriptions */
-    for (tmp = group->objects; tmp != NULL; tmp = tmp->next) {
-      const PropDescription *desc = NULL;
-      DiaObject *obj = tmp->data;
-
-      desc = object_get_prop_descriptions(obj);
-
-      if (desc) descs = g_list_append(descs, (gpointer)desc);
-    }
-    group->pdesc = prop_desc_lists_union(descs);
-    g_list_free(descs); /* XXX: haven't we got a leak here ? */
+
+    /* create list of property descriptions. this must be freed by the
+       DestroyFunc. */
+    group->pdesc = object_list_get_prop_descriptions(group->objects, PROP_UNION);
 
     if (group->pdesc != NULL) {
       /* hijack event delivery */

=== modified file 'lib/object.h'
--- lib/object.h	2008-04-27 14:22:12 +0000
+++ lib/object.h	2008-12-03 12:02:10 +0000
@@ -285,7 +285,9 @@
  *  This is one of the object_ops functions.
  * @param obj The object whose properties we want described.
  * @return a NULL-terminated array of property descriptions.
- * As the const return implies the returned data is not owned by  the caller.
+ * As the const return implies the returned data is not owned by  the
+ * caller. If this function returns a dynamically created description,
+ * then DestroyFunc must free the description.
  */
 typedef const PropDescription *(* DescribePropsFunc) (DiaObject *obj);
 

=== modified file 'lib/propdesc.c'
--- lib/propdesc.c	2008-04-13 15:30:56 +0000
+++ lib/propdesc.c	2008-12-03 14:36:00 +0000
@@ -128,26 +128,35 @@
   GList *tmp;
 
   /* make sure the array is allocated */
+  /* FIXME: this doesn't seem to do anything useful if an error occurs. */
   g_array_append_val(arr, null_prop_desc);
   g_array_remove_index(arr, 0);
 
+  /* Each element in the list is a GArray of PropDescription,
+     terminated by a NULL element. */
   for (tmp = plists; tmp; tmp = tmp->next) {
     PropDescription *plist = tmp->data;
     int i;
 
     for (i = 0; plist[i].name != NULL; i++) {
       int j;
-#if 1
+
       if (plist[i].flags & PROP_FLAG_DONT_MERGE)
-        continue; /* even union must not conatin anything which can't be merged */
-#endif
+        continue; /* exclude anything that can't be merged */
+
+      /* Check to see if this PropDescription is already included in
+	 the union. */
       for (j = 0; j < arr->len; j++)
 	if (g_array_index(arr, PropDescription, j).quark == plist[i].quark)
 	  break;
+
+      /* Add to the union if it isn't already present. */
       if (j == arr->len)
 	g_array_append_val(arr, plist[i]);
     }
   }
+
+  /* Get the actually array and free the GArray wrapper. */
   ret = (PropDescription *)arr->data;
   g_array_free(arr, FALSE);
   return ret;
@@ -181,6 +190,8 @@
   g_array_remove_index(arr, 0);
 
   if (plists) {
+
+    /* initialise the intersection with the first list. */
     ret = plists->data;
     for (i = 0; ret[i].name != NULL; i++)
       g_array_append_val(arr, ret[i]);
@@ -192,6 +203,8 @@
       /* go through array in reverse so that removals don't stuff things up */
       for (i = arr->len - 1; i >= 0; i--) {
 	gint j;
+	/* FIXME: we can avoid a copy here by making cand a pointer to
+	   the data in the GArray. */
         PropDescription cand = g_array_index(arr,PropDescription,i);
         gboolean remove = TRUE;
 	for (j = 0; ret[j].name != NULL; j++) {

=== modified file 'lib/properties.h'
--- lib/properties.h	2008-04-25 13:04:32 +0000
+++ lib/properties.h	2008-12-03 14:20:38 +0000
@@ -239,6 +239,8 @@
 #define PROP_FLAG_WIDGET_ONLY 0x0080 /* only cosmetic property, no data */
 #define PROP_FLAG_OPTIONAL 0x0100 /* don't complain if it does not exist */
 
+typedef enum {PROP_UNION, PROP_INTERSECTION} PropMergeOption;
+
 #define PROP_DESC_END { NULL, 0, 0, NULL, NULL, NULL, 0 }
 
 /* extra data pointers for various property types */
@@ -407,6 +409,8 @@
 /* will do whatever is needed to make the PropDescription * list look good to 
    the rest of the properties code. Can return NULL. */
 const PropDescription *object_get_prop_descriptions(const DiaObject *obj);
+const PropDescription *object_list_get_prop_descriptions(GList *objects,
+							 PropMergeOption option);
 
 gboolean object_get_props_from_offsets(DiaObject *obj, PropOffset *offsets,
 				       GPtrArray *props);

=== modified file 'lib/propobject.c'
--- lib/propobject.c	2008-05-16 16:40:13 +0000
+++ lib/propobject.c	2008-12-03 14:43:50 +0000
@@ -52,6 +52,31 @@
   return pdesc;
 }
 
+const PropDescription *
+object_list_get_prop_descriptions(GList *objects, PropMergeOption option)
+{
+  GList *descs = NULL, *tmp;
+  const PropDescription *pdesc;
+
+  for (tmp = objects; tmp != NULL; tmp = tmp->next) {
+    DiaObject *obj = tmp->data;
+    const PropDescription *desc = object_get_prop_descriptions(obj);
+
+    if (desc) descs = g_list_append(descs, (gpointer)desc);
+  }
+
+  if (option == PROP_UNION)
+    pdesc = prop_desc_lists_union(descs);
+  else
+    pdesc = prop_desc_lists_intersection(descs);
+
+  /* Important: Do not destroy the actual descriptions returned by the
+     objects. We don't own them. */
+  g_list_free(descs);
+
+  return pdesc;
+}
+
 
 /* ------------------------------------------------------ */
 /* Change management                                      */

