=== modified file 'app/commands.c'
--- app/commands.c	2008-08-09 11:28:03 +0000
+++ app/commands.c	2008-12-07 07:16:35 +0000
@@ -1106,8 +1106,7 @@
   if (!dia || textedit_mode(ddisplay_active())) return;
 
   if (dia->data->selected != NULL) {
-    selected = dia->data->selected->data;
-    properties_show(dia, selected);
+    object_list_properties_show(dia, dia->data->selected);
   } else {
     diagram_properties_show(dia);
   } 

=== modified file 'app/diagram_tree.c'
--- app/diagram_tree.c	2008-07-18 21:00:55 +0000
+++ app/diagram_tree.c	2008-12-07 07:16:35 +0000
@@ -516,7 +516,7 @@
       Diagram *dia = (Diagram *)gtk_ctree_node_get_row_data(tree->tree, parent);
       DiaObject *obj =
 	(DiaObject *)gtk_ctree_node_get_row_data(tree->tree, tree->last);
-      properties_show(dia, obj);
+      object_properties_show(dia, obj);
     }
   }
 }

=== modified file 'app/modify_tool.c'
--- app/modify_tool.c	2008-07-13 17:31:48 +0000
+++ app/modify_tool.c	2008-12-07 10:14:26 +0000
@@ -308,7 +308,7 @@
   clicked_obj = click_select_object(ddisp, &clickedpoint, event);
   
   if ( clicked_obj != NULL ) {
-    properties_show(ddisp->diagram, clicked_obj);
+    object_list_properties_show(ddisp->diagram, ddisp->diagram->data->selected);
   } else { /* No object selected */
     /*printf("didn't select object\n");*/
     if (!(event->state & GDK_SHIFT_MASK)) {

=== modified file 'app/properties.c'
--- app/properties.c	2008-07-18 21:00:55 +0000
+++ app/properties.c	2008-12-07 07:16:35 +0000
@@ -34,7 +34,7 @@
 static GtkWidget *dialog = NULL;
 static GtkWidget *dialog_vbox = NULL;
 static GtkWidget *object_part = NULL;
-static DiaObject *current_obj = NULL;
+static GList *current_objects = NULL;
 static Diagram *current_dia = NULL;
 
 static GtkWidget *no_properties_dialog = NULL;
@@ -45,7 +45,7 @@
 static gboolean properties_key_event(GtkWidget *widget,
 				     GdkEventKey *event,
 				     gpointer data);
-static void properties_dialog_hide();
+static void properties_dialog_hide(void);
 
 static void create_dialog()
 {
@@ -83,24 +83,6 @@
   g_object_ref_sink (G_OBJECT (no_properties_dialog));
 }
 
-static gint
-properties_part_destroyed(GtkWidget *widget, gpointer data)
-{
-  if (widget == object_part) {
-    object_part = NULL;
-    current_obj = NULL;
-    current_dia = NULL;
-  }
-  return 0;
-}
-
-static gint
-properties_dialog_destroyed(GtkWidget *widget, gpointer data)
-{
-  dialog = NULL;
-  return 0;
-}
-
 static gboolean
 properties_key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
 {
@@ -126,27 +108,33 @@
                    gpointer   data)
 {
   ObjectChange *obj_change = NULL;
+  gboolean set_tp = TRUE;
+  GList *tmp;
 
   if (   response_id == GTK_RESPONSE_APPLY 
       || response_id == GTK_RESPONSE_OK) {
-    if ((current_obj != NULL) && (current_dia != NULL)) {
-      object_add_updates(current_obj, current_dia);
-      obj_change = current_obj->ops->apply_properties_from_dialog(current_obj, object_part);
-      object_add_updates(current_obj, current_dia);
+    if ((current_objects != NULL) && (current_dia != NULL)) {
+      object_add_updates_list(current_objects, current_dia);
 
-      diagram_update_connections_object(current_dia, current_obj, TRUE);
+      for (tmp = current_objects; tmp != NULL; tmp = tmp->next) {
+	DiaObject *current_obj = (DiaObject*)tmp->data;
+	obj_change = current_obj->ops->apply_properties_from_dialog(current_obj, object_part);
+	object_add_updates(current_obj, current_dia);
+	diagram_update_connections_object(current_dia, current_obj, TRUE);
     
-      if (obj_change != NULL) {
-	undo_object_change(current_dia, current_obj, obj_change);
+	if (obj_change != NULL) {
+	  undo_object_change(current_dia, current_obj, obj_change);
+	  set_tp = set_tp && TRUE;
+	} else
+	  set_tp = FALSE;
+
+	diagram_object_modified(current_dia, current_obj);
       }
     
       diagram_modified(current_dia);
-
-      diagram_object_modified(current_dia, current_obj);
-
       diagram_update_extents(current_dia);
       
-      if (obj_change != NULL) {
+      if (set_tp) {
 	undo_set_transactionpoint(current_dia->undo);
       }  else {
 	message_warning(_("This object doesn't support Undo/Redo.\n"
@@ -186,41 +174,52 @@
   }
 }
 
-void
-properties_show(Diagram *dia, DiaObject *obj)
-{
-  GtkWidget *properties = NULL;
-
-  if (obj != NULL) 
-    properties = obj->ops->get_properties(obj, FALSE);
-
-  if (dialog == NULL)
+static void
+clear_dialog_globals()
+{
+  if (object_part != NULL) {
+    gtk_container_remove(GTK_CONTAINER(dialog_vbox), object_part);
+    object_part = NULL;
+  }
+  g_list_free(current_objects);
+  current_objects = NULL;
+  current_dia = NULL;
+}
+
+void
+object_properties_show(Diagram *dia, DiaObject *obj)
+{
+  GList *tmp = NULL;
+  tmp = g_list_append(tmp, obj);
+  object_list_properties_show(dia, tmp);
+  g_list_free(tmp);
+}
+
+void
+object_list_properties_show(Diagram *dia, GList *objects)
+{
+  GtkWidget *properties;
+
+  if (!dialog)
     create_dialog();
+  clear_dialog_globals();
 
-  if (obj==NULL) {
+  if (!objects) {
     /* Hide dialog when no object is selected */
     properties_dialog_hide();
     return;
   }
 
-  if (properties == NULL) { /* No properties or no object */
+  properties = object_list_create_props_dialog(objects, FALSE);
+  if (properties == NULL) {
     properties = no_properties_dialog;
-    obj = NULL;
-    dia = NULL;
-  }
-
-  if (object_part != NULL) {
-    gtk_container_remove(GTK_CONTAINER(dialog_vbox), object_part);
-    object_part = NULL;
-    current_obj = NULL;
-    current_dia = NULL;
-  }
-
-  if (obj != NULL) {
-    DiaObjectType *otype;
+  }
+
+  if (g_list_length(objects) == 1) {
+    DiaObject *obj = (DiaObject*)objects->data;
+    DiaObjectType *otype = obj->type;
     gchar *buf;
-
-    otype = obj->type;
+    
     buf = g_strconcat(_("Properties: "), otype->name, NULL);
     gtk_window_set_title(GTK_WINDOW(dialog), buf);
     g_free(buf);
@@ -228,46 +227,60 @@
     gtk_window_set_title(GTK_WINDOW(dialog), _("Object properties:"));
   }
 
-  g_signal_connect (G_OBJECT (properties), "destroy",
-		  G_CALLBACK(properties_part_destroyed), NULL);
-  g_signal_connect (G_OBJECT (dialog), "destroy",
-		  G_CALLBACK(properties_dialog_destroyed), NULL);
-
   gtk_box_pack_start(GTK_BOX(dialog_vbox), properties, TRUE, TRUE, 0);
 
   gtk_widget_show (properties);
 
   properties_give_focus(properties, NULL);
 
-  if (obj != current_obj)
-    gtk_window_resize (GTK_WINDOW(dialog), 1, 1); /* resize to minimum */
+  /* resize to minimum */
+  /* if (obj != current_obj) */
+  gtk_window_resize (GTK_WINDOW(dialog), 1, 1);
   gtk_window_set_transient_for(GTK_WINDOW(dialog),
 			       GTK_WINDOW (ddisplay_active()->shell));
   gtk_window_present (GTK_WINDOW (dialog));
   object_part = properties;
-  current_obj = obj;
+  current_objects = g_list_copy(objects);
   current_dia = dia;
 }
 
-void properties_dialog_hide()
+void
+properties_dialog_hide(void)
 {
-  if (dialog)
-    gtk_widget_hide(dialog);
-  current_obj = NULL;
+  if (!dialog)
+    return;
+  clear_dialog_globals();
+  gtk_widget_hide(dialog);
 }
 
 void
 properties_hide_if_shown(Diagram *dia, DiaObject *obj)
 {
-  if (current_obj == obj) {
-    properties_show(dia, NULL);
+  if (g_list_find(current_objects, obj)) {
+    if (g_list_length(current_objects) == 1)
+      /* If obj is the only object shown, then we hide the dialog. */
+      properties_dialog_hide();
+    else {
+      /* else we remove this object and recreate the dialog. */
+      GList *tmp = g_list_copy(current_objects);
+      tmp = g_list_remove(tmp, obj);
+      object_list_properties_show(dia, tmp);
+      g_list_free(tmp);
+    }
   }
 }
 
 void
 properties_update_if_shown(Diagram *dia, DiaObject *obj)
 {
-  if (current_obj == obj) {
-    properties_show(dia, obj);
+  /* if there are multiple objects currently shown, there is no reason
+     why the properties of this object should be updated in the
+     dialog. */
+  if (g_list_length(current_objects) != 1)
+    return;
+  else {
+    /* FIXME: instead of recreating the dialog, there should be a way
+       to refresh the existing PropDialog widget. */
+    object_properties_show(dia, (DiaObject*)current_objects->data);
   }
 }

=== modified file 'app/properties.h'
--- app/properties.h	2004-05-21 15:48:36 +0000
+++ app/properties.h	2008-12-07 07:14:09 +0000
@@ -21,7 +21,8 @@
 #include "object.h"
 #include "diagram.h"
 
-void properties_show(Diagram *dia, DiaObject *obj);
+void object_properties_show(Diagram *dia, DiaObject *obj);
+void object_list_properties_show(Diagram *dia, GList *objects);
 void properties_update_if_shown(Diagram *dia, DiaObject *obj);
 void properties_hide_if_shown(Diagram *dia, DiaObject *obj);
 

=== modified file 'lib/propdialogs.c'
--- lib/propdialogs.c	2008-07-18 21:00:55 +0000
+++ lib/propdialogs.c	2008-12-07 10:49:54 +0000
@@ -38,16 +38,16 @@
 const gchar *prop_dialogdata_key = "object-props:dialogdata";
 
 static void prop_dialog_signal_destroy(GtkWidget *dialog_widget);
-static void prop_dialog_fill(PropDialog *dialog, DiaObject *obj, gboolean is_default);
+static void prop_dialog_fill(PropDialog *dialog, GList *objects, gboolean is_default);
 
 PropDialog *
-prop_dialog_new(DiaObject *obj, gboolean is_default) 
+prop_dialog_new(GList *objects, gboolean is_default) 
 {
   PropDialog *dialog = g_new0(PropDialog,1);
   dialog->props = NULL;
   dialog->widget = gtk_vbox_new(FALSE,1);
   dialog->prop_widgets = g_array_new(FALSE,TRUE,sizeof(PropWidgetAssoc));
-  dialog->obj_copy = NULL;
+  dialog->copies = NULL;
   dialog->curtable = NULL;
   dialog->containers = g_ptr_array_new();
 
@@ -57,7 +57,7 @@
   g_signal_connect (G_OBJECT (dialog->widget), "destroy",
                     G_CALLBACK (prop_dialog_signal_destroy), NULL);
 
-  prop_dialog_fill(dialog,obj,is_default);
+  prop_dialog_fill(dialog,objects,is_default);
 
   return dialog;
 }
@@ -68,7 +68,7 @@
   if (dialog->props) prop_list_free(dialog->props);
   g_array_free(dialog->prop_widgets,TRUE);
   g_ptr_array_free(dialog->containers,TRUE);
-  if (dialog->obj_copy) dialog->obj_copy->ops->destroy(dialog->obj_copy);
+  if (dialog->copies) destroy_object_list(dialog->copies);
   g_free(dialog);
 }
 
@@ -173,13 +173,11 @@
   if (ped) {
     PropDialog *dialog = ped->dialog;
     Property *prop = ped->self;
-    DiaObject *obj = dialog->obj_copy;
+    GList *list, *tmp;
     guint j;
 
-    g_assert(obj);
-    g_assert(object_complies_with_stdprop(obj));
-    g_assert(obj->ops->set_props);
-    g_assert(obj->ops->get_props);
+    list = dialog->copies;
+    g_return_if_fail(list);
 
     prop->experience &= ~PXP_NOTSET;
 
@@ -192,20 +190,18 @@
     /* obj is a scratch object ; we can do what we want with it. */
     prop_get_data_from_widgets(dialog);
 
-    obj->ops->set_props(obj,dialog->props);
-    prop->event_handler(obj,prop);
-    obj->ops->get_props(obj,dialog->props);
+    for (tmp = list; tmp != NULL; tmp = tmp->next) {
+      DiaObject *obj = (DiaObject*)tmp->data;
+      obj->ops->set_props(obj,dialog->props);
+      prop->event_handler(obj,prop);
+      obj->ops->get_props(obj,dialog->props);
+    }
 
     for (j = 0; j < dialog->prop_widgets->len; j++) {
       PropWidgetAssoc *pwa = 
         &g_array_index(dialog->prop_widgets,PropWidgetAssoc,j);
-      /*
-      g_message("going to reset widget %p for prop %s/%s "
-                "[in response to event on  %s/%s]",
-                pwa->widget,pwa->prop->type,pwa->prop->name,
-                prop->type,prop->name); */ 
       pwa->prop->ops->reset_widget(pwa->prop,pwa->widget);
-    } 
+    }
   } else {
     g_assert_not_reached();
   }
@@ -216,21 +212,11 @@
                     GtkObject *object,
                     const gchar *signal)
 {
-  DiaObject *obj = prop->self.dialog->obj_copy;
-
   if (0==strcmp(signal,"FIXME")) {
     g_warning("signal type unknown for this kind of property (name is %s), \n"
               "handler ignored.",prop->name);
     return;
   } 
-  if ((!obj->ops->set_props) || (!obj->ops->get_props)) {
-      g_warning("object has no [sg]et_props() routine(s).\n"
-                "event handler for property %s ignored.",
-                prop->name);
-      return;
-  }
-  
-  /* g_message("connected signal %s to property %s",signal,prop->name); */
 
   g_signal_connect (G_OBJECT (object),
                     signal,
@@ -245,9 +231,6 @@
   PropWidgetAssoc pwa;
   GtkWidget *label;
 
-  if (!dialog->obj_copy)
-    dialog->obj_copy = dialog->orig_obj->ops->copy(dialog->orig_obj);
-
   prop->self.dialog = dialog;
   prop->self.self = prop;
   prop->self.my_index = dialog->prop_widgets->len;
@@ -326,15 +309,17 @@
 }
 
 static void 
-prop_dialog_fill(PropDialog *dialog, DiaObject *obj, gboolean is_default) {
+prop_dialog_fill(PropDialog *dialog, GList *objects, gboolean is_default)
+{
   const PropDescription *pdesc;
   GPtrArray *props;
 
-  g_return_if_fail(object_complies_with_stdprop(obj));
-
-  dialog->orig_obj = obj;
-
-  pdesc = object_get_prop_descriptions(obj);
+  g_return_if_fail(objects_comply_with_stdprop(objects));
+
+  dialog->objects = g_list_copy(objects);
+  dialog->copies = object_copy_list(objects);
+
+  pdesc = object_list_get_prop_descriptions(objects, PROP_UNION);
   if (!pdesc) return;
 
   if (is_default)
@@ -345,7 +330,7 @@
   if (!props) return;
     
   dialog->props = props;
-  obj->ops->get_props(obj,props);
+  object_list_get_props(objects, props);
 
   prop_dialog_add_properties(dialog, props);
 }

=== modified file 'lib/properties.h'
--- lib/properties.h	2008-12-06 12:47:14 +0000
+++ lib/properties.h	2008-12-07 06:23:19 +0000
@@ -64,9 +64,8 @@
   GArray *prop_widgets; /* of PropWidgetAssoc. This is a "flat" listing of 
                            properties and widgets (no nesting information is 
                            kept here) */
-  DiaObject *obj_copy; /* if !NULL, a copy of the object which can be used 
-                       as scratch. */
-  DiaObject *orig_obj; /* The original object (do not touch !) */
+  GList *objects; /* a copy of the objects, to be used as scratch. */
+  GList *copies;  /* The original objects. */
 
   GPtrArray *containers;
   WIDGET *lastcont;
@@ -405,6 +404,9 @@
 /* returns TRUE if this object can be handled (at least in part) through this
    library. */
 gboolean object_complies_with_stdprop(const DiaObject *obj);
+gboolean objects_comply_with_stdprop(GList *objects);
+
+void object_list_get_props(GList *objects, GPtrArray *props);
 
 /* will do whatever is needed to make the PropDescription * list look good to 
    the rest of the properties code. Can return NULL. */
@@ -425,6 +427,7 @@
  * If is_default is set, this is a default dialog, not an object dialog.
  */
 WIDGET *object_create_props_dialog     (DiaObject *obj, gboolean is_default);
+WIDGET *object_list_create_props_dialog(GList *obj, gboolean is_default);
 ObjectChange *object_apply_props_from_dialog (DiaObject *obj, WIDGET *dialog);
 
 /* create a property from the object's property descriptors. To be freed with

=== modified file 'lib/propinternals.h'
--- lib/propinternals.h	2004-11-26 21:02:40 +0000
+++ lib/propinternals.h	2008-12-07 10:47:01 +0000
@@ -64,7 +64,7 @@
 GtkWidget *prop_dialog_container_pop(PropDialog *dialog);
 void prop_dialog_add_property(PropDialog *dialog, Property *prop);
 
-PropDialog *prop_dialog_new(DiaObject *obj, gboolean is_default);
+PropDialog *prop_dialog_new(GList *objects, gboolean is_default);
 PropDialog *prop_dialog_from_widget(GtkWidget *dialog_widget);
 void prop_dialog_destroy(PropDialog *dialog);
 void prop_get_data_from_widgets(PropDialog *dialog);

=== modified file 'lib/propobject.c'
--- lib/propobject.c	2008-12-06 12:47:14 +0000
+++ lib/propobject.c	2008-12-07 10:45:52 +0000
@@ -174,7 +174,15 @@
 WIDGET *
 object_create_props_dialog(DiaObject *obj, gboolean is_default)
 {
-  return prop_dialog_new(obj, is_default)->widget;
+  GList *list = NULL;
+  list = g_list_append(list, obj);
+  return object_list_create_props_dialog(list, is_default);
+}
+
+WIDGET *
+object_list_create_props_dialog(GList *objects, gboolean is_default)
+{
+  return prop_dialog_new(objects, is_default)->widget;
 }
 
 
@@ -192,6 +200,19 @@
     return obj->ops->apply_properties_list(obj, dialog->props);
 }
 
+gboolean
+objects_comply_with_stdprop(GList *objects)
+{
+  GList *tmp = objects;
+
+  for (; tmp != NULL; tmp = tmp->next) {
+    const DiaObject *obj = (const DiaObject*)tmp->data;
+    if (!object_complies_with_stdprop(obj))
+      return FALSE;
+  }
+
+  return TRUE;
+}
 
 gboolean 
 object_complies_with_stdprop(const DiaObject *obj) 
@@ -215,6 +236,17 @@
   return TRUE;
 }
 
+void
+object_list_get_props(GList *objects, GPtrArray *props)
+{
+  GList *tmp = objects;
+
+  for (; tmp != NULL; tmp = tmp->next) {
+    DiaObject *obj = (DiaObject*)tmp->data;
+    obj->ops->get_props(obj,props);
+  }
+}
+
 static gboolean
 pdtpp_do_save_no_standard_default (const PropDescription *pdesc)
 {

