]> git.0d.be Git - empathy.git/commitdiff
Merge branch 'trivia'
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Fri, 27 Aug 2010 09:57:14 +0000 (11:57 +0200)
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Fri, 27 Aug 2010 09:57:14 +0000 (11:57 +0200)
libempathy-gtk/empathy-individual-linker.c
libempathy-gtk/empathy-individual-view.c
libempathy-gtk/empathy-individual-view.h
libempathy-gtk/empathy-persona-view.c
libempathy-gtk/empathy-persona-view.h
libempathy/empathy-individual-manager.c
src/empathy-main-window.c

index 9ebb4718b4fcadb1b0e6fcfb3e8190bdb2581c26..b4226a71ce217e7ba8606486ffa55cda44027681 100644 (file)
@@ -216,6 +216,78 @@ row_toggled_cb (GtkCellRendererToggle *cell_renderer,
   gtk_tree_path_free (tree_path);
 }
 
+static gboolean
+individual_view_drag_motion_cb (GtkWidget *widget,
+    GdkDragContext *context,
+    gint x,
+    gint y,
+    guint time_)
+{
+  EmpathyIndividualView *view = EMPATHY_INDIVIDUAL_VIEW (widget);
+  GdkAtom target;
+
+  target = gtk_drag_dest_find_target (GTK_WIDGET (view), context, NULL);
+
+  if (target == gdk_atom_intern_static_string ("text/persona-id"))
+    {
+      GtkTreePath *path;
+
+      /* FIXME: It doesn't make sense for us to highlight a specific row or
+       * position to drop a Persona in, so just highlight the entire widget.
+       * Since I can't find a way to do this, just highlight the first possible
+       * position in the tree. */
+      gdk_drag_status (context, gdk_drag_context_get_suggested_action (context),
+          time_);
+
+      path = gtk_tree_path_new_first ();
+      gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (view), path,
+          GTK_TREE_VIEW_DROP_BEFORE);
+      gtk_tree_path_free (path);
+
+      return TRUE;
+    }
+
+  /* Unknown or unhandled drag target */
+  gdk_drag_status (context, GDK_ACTION_DEFAULT, time_);
+  gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (view), NULL, 0);
+
+  return FALSE;
+}
+
+static gboolean
+individual_view_drag_persona_received_cb (EmpathyIndividualView *view,
+    GdkDragAction action,
+    FolksPersona *persona,
+    FolksIndividual *individual,
+    EmpathyIndividualLinker *self)
+{
+  EmpathyIndividualLinkerPriv *priv = GET_PRIV (self);
+
+  /* A Persona has been dragged onto the EmpathyIndividualView (from the
+   * EmpathyPersonaView), so we try to remove the Individual which contains
+   * the Persona from the link. */
+  if (individual != priv->start_individual)
+    {
+      unlink_individual (self, individual);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+persona_view_drag_individual_received_cb (EmpathyPersonaView *view,
+    GdkDragAction action,
+    FolksIndividual *individual,
+    EmpathyIndividualLinker *self)
+{
+  /* An Individual has been dragged onto the EmpathyPersonaView (from the
+   * EmpathyIndividualView), so we try to add the Individual to the link. */
+  link_individual (self, individual);
+
+  return TRUE;
+}
+
 static void
 set_up (EmpathyIndividualLinker *self)
 {
@@ -258,11 +330,18 @@ set_up (EmpathyIndividualLinker *self)
   empathy_individual_store_set_show_protocols (priv->individual_store, FALSE);
 
   priv->individual_view = empathy_individual_view_new (priv->individual_store,
-      EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
+      EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DRAG |
+      EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DROP |
+      EMPATHY_INDIVIDUAL_VIEW_FEATURE_PERSONA_DROP,
+      EMPATHY_INDIVIDUAL_FEATURE_NONE);
   empathy_individual_view_set_show_offline (priv->individual_view, TRUE);
 
   g_signal_connect (priv->individual_view, "row-activated",
       (GCallback) row_activated_cb, self);
+  g_signal_connect (priv->individual_view, "drag-motion",
+      (GCallback) individual_view_drag_motion_cb, self);
+  g_signal_connect (priv->individual_view, "drag-persona-received",
+      (GCallback) individual_view_drag_persona_received_cb, self);
 
   /* Add a checkbox column to the selector */
   cell_renderer = gtk_cell_renderer_toggle_new ();
@@ -322,9 +401,13 @@ set_up (EmpathyIndividualLinker *self)
 
   priv->persona_store = empathy_persona_store_new (priv->new_individual);
   empathy_persona_store_set_show_protocols (priv->persona_store, TRUE);
-  persona_view = empathy_persona_view_new (priv->persona_store);
+  persona_view = empathy_persona_view_new (priv->persona_store,
+      EMPATHY_PERSONA_VIEW_FEATURE_ALL);
   empathy_persona_view_set_show_offline (persona_view, TRUE);
 
+  g_signal_connect (persona_view, "drag-individual-received",
+      (GCallback) persona_view_drag_individual_received_cb, self);
+
   gtk_container_add (GTK_CONTAINER (scrolled_window),
       GTK_WIDGET (persona_view));
   gtk_widget_show (GTK_WIDGET (persona_view));
index 6767c3bfde9ad57d55596739f97673b30ec0387e..b8365d175040aca34f232b0af240edf70713c410 100644 (file)
@@ -68,9 +68,8 @@ typedef struct
   EmpathyIndividualStore *store;
   GtkTreeRowReference *drag_row;
   EmpathyIndividualViewFeatureFlags view_features;
-  EmpathyContactFeatureFlags individual_features;
+  EmpathyIndividualFeatureFlags individual_features;
   GtkWidget *tooltip_widget;
-  GtkTargetList *file_targets;
 
   gboolean show_offline;
 
@@ -110,6 +109,7 @@ enum
 enum DndDragType
 {
   DND_DRAG_TYPE_INDIVIDUAL_ID,
+  DND_DRAG_TYPE_PERSONA_ID,
   DND_DRAG_TYPE_URI_LIST,
   DND_DRAG_TYPE_STRING,
 };
@@ -118,20 +118,16 @@ enum DndDragType
   { (gchar *) T, 0, I }
 
 static const GtkTargetEntry drag_types_dest[] = {
+  DRAG_TYPE ("text/individual-id", DND_DRAG_TYPE_INDIVIDUAL_ID),
+  DRAG_TYPE ("text/persona-id", DND_DRAG_TYPE_PERSONA_ID),
   DRAG_TYPE ("text/path-list", DND_DRAG_TYPE_URI_LIST),
   DRAG_TYPE ("text/uri-list", DND_DRAG_TYPE_URI_LIST),
-  DRAG_TYPE ("text/contact-id", DND_DRAG_TYPE_INDIVIDUAL_ID),
   DRAG_TYPE ("text/plain", DND_DRAG_TYPE_STRING),
   DRAG_TYPE ("STRING", DND_DRAG_TYPE_STRING),
 };
 
-static const GtkTargetEntry drag_types_dest_file[] = {
-  DRAG_TYPE ("text/path-list", DND_DRAG_TYPE_URI_LIST),
-  DRAG_TYPE ("text/uri-list", DND_DRAG_TYPE_URI_LIST),
-};
-
 static const GtkTargetEntry drag_types_source[] = {
-  DRAG_TYPE ("text/contact-id", DND_DRAG_TYPE_INDIVIDUAL_ID),
+  DRAG_TYPE ("text/individual-id", DND_DRAG_TYPE_INDIVIDUAL_ID),
 };
 
 #undef DRAG_TYPE
@@ -141,7 +137,8 @@ static GdkAtom drag_atoms_source[G_N_ELEMENTS (drag_types_source)];
 
 enum
 {
-  DRAG_CONTACT_RECEIVED,
+  DRAG_INDIVIDUAL_RECEIVED,
+  DRAG_PERSONA_RECEIVED,
   LAST_SIGNAL
 };
 
@@ -247,44 +244,6 @@ groups_change_group_cb (GObject *source,
     }
 }
 
-static void
-individual_view_handle_drag (EmpathyIndividualView *self,
-    FolksIndividual *individual,
-    const gchar *old_group,
-    const gchar *new_group,
-    GdkDragAction action)
-{
-  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (self));
-  g_return_if_fail (FOLKS_IS_INDIVIDUAL (individual));
-
-  DEBUG ("individual %s dragged from '%s' to '%s'",
-      folks_individual_get_id (individual), old_group, new_group);
-
-  if (!tp_strdiff (new_group, EMPATHY_INDIVIDUAL_STORE_FAVORITE))
-    {
-      /* Mark contact as favourite */
-      folks_favourite_set_is_favourite (FOLKS_FAVOURITE (individual), TRUE);
-      return;
-    }
-
-  if (!tp_strdiff (old_group, EMPATHY_INDIVIDUAL_STORE_FAVORITE))
-    {
-      /* Remove contact as favourite */
-      folks_favourite_set_is_favourite (FOLKS_FAVOURITE (individual), FALSE);
-
-      /* Don't try to remove it */
-      old_group = NULL;
-    }
-
-  if (new_group != NULL)
-    folks_groups_change_group (FOLKS_GROUPS (individual), new_group, TRUE,
-        groups_change_group_cb, NULL);
-
-  if (old_group != NULL && action == GDK_ACTION_MOVE)
-    folks_groups_change_group (FOLKS_GROUPS (individual), old_group, FALSE,
-        groups_change_group_cb, NULL);
-}
-
 static gboolean
 group_can_be_modified (const gchar *name,
     gboolean is_fake_group,
@@ -307,20 +266,20 @@ group_can_be_modified (const gchar *name,
 }
 
 static gboolean
-individual_view_contact_drag_received (GtkWidget *self,
+individual_view_individual_drag_received (GtkWidget *self,
     GdkDragContext *context,
     GtkTreeModel *model,
     GtkTreePath *path,
     GtkSelectionData *selection)
 {
   EmpathyIndividualViewPriv *priv;
-  EmpathyIndividualManager *manager;
+  EmpathyIndividualManager *manager = NULL;
   FolksIndividual *individual;
   GtkTreePath *source_path;
   const gchar *sel_data;
   gchar *new_group = NULL;
   gchar *old_group = NULL;
-  gboolean new_group_is_fake, old_group_is_fake = TRUE;
+  gboolean new_group_is_fake, old_group_is_fake = TRUE, retval = FALSE;
 
   priv = GET_PRIV (self);
 
@@ -329,10 +288,16 @@ individual_view_contact_drag_received (GtkWidget *self,
       NULL, &new_group_is_fake);
 
   if (!group_can_be_modified (new_group, new_group_is_fake, TRUE))
-    return FALSE;
-
-  /* Get source group information. */
-  if (priv->drag_row)
+    goto finished;
+
+  /* Get source group information iff the view has the FEATURE_GROUPS_CHANGE
+   * feature. Otherwise, we just add the dropped contact to whichever group
+   * they were dropped in, and don't remove them from their old group. This
+   * allows for Individual views which shouldn't allow Individuals to have
+   * their groups changed, and also for dragging Individuals between Individual
+   * views. */
+  if ((priv->view_features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_CHANGE) &&
+      priv->drag_row != NULL)
     {
       source_path = gtk_tree_row_reference_get_path (priv->drag_row);
       if (source_path)
@@ -342,16 +307,19 @@ individual_view_contact_drag_received (GtkWidget *self,
               NULL, &old_group_is_fake);
           gtk_tree_path_free (source_path);
         }
-    }
 
-  if (!group_can_be_modified (old_group, old_group_is_fake, FALSE))
-    return FALSE;
+      if (!group_can_be_modified (old_group, old_group_is_fake, FALSE))
+        goto finished;
 
-  if (!tp_strdiff (old_group, new_group))
+      if (!tp_strdiff (old_group, new_group))
+        goto finished;
+    }
+  else if (priv->drag_row != NULL)
     {
-      g_free (new_group);
-      g_free (old_group);
-      return FALSE;
+      /* We don't allow changing Individuals' groups, and this Individual was
+       * dragged from another group in *this* Individual view, so we disallow
+       * the drop. */
+      goto finished;
     }
 
   /* XXX: for contacts, we used to ensure the account, create the contact
@@ -364,23 +332,130 @@ individual_view_contact_drag_received (GtkWidget *self,
   if (individual == NULL)
     {
       DEBUG ("failed to find drag event individual with ID '%s'", sel_data);
-
-      g_object_unref (manager);
-
-      return FALSE;
+      goto finished;
     }
 
   /* FIXME: We should probably wait for the cb before calling
    * gtk_drag_finish */
 
-  individual_view_handle_drag (EMPATHY_INDIVIDUAL_VIEW (self), individual,
-      old_group, new_group, gdk_drag_context_get_selected_action (context));
+  /* Emit a signal notifying of the drag. We change the Individual's groups in
+   * the default signal handler. */
+  g_signal_emit (self, signals[DRAG_INDIVIDUAL_RECEIVED], 0,
+      gdk_drag_context_get_selected_action (context), individual, new_group,
+      old_group);
+
+  retval = TRUE;
 
-  g_object_unref (G_OBJECT (manager));
+finished:
+  tp_clear_object (&manager);
   g_free (old_group);
   g_free (new_group);
 
-  return TRUE;
+  return retval;
+}
+
+static void
+real_drag_individual_received_cb (EmpathyIndividualView *self,
+    GdkDragAction action,
+    FolksIndividual *individual,
+    const gchar *new_group,
+    const gchar *old_group)
+{
+  DEBUG ("individual %s dragged from '%s' to '%s'",
+      folks_individual_get_id (individual), old_group, new_group);
+
+  if (!tp_strdiff (new_group, EMPATHY_INDIVIDUAL_STORE_FAVORITE))
+    {
+      /* Mark contact as favourite */
+      folks_favourite_set_is_favourite (FOLKS_FAVOURITE (individual), TRUE);
+      return;
+    }
+
+  if (!tp_strdiff (old_group, EMPATHY_INDIVIDUAL_STORE_FAVORITE))
+    {
+      /* Remove contact as favourite */
+      folks_favourite_set_is_favourite (FOLKS_FAVOURITE (individual), FALSE);
+
+      /* Don't try to remove it */
+      old_group = NULL;
+    }
+
+  if (new_group != NULL)
+    {
+      folks_groups_change_group (FOLKS_GROUPS (individual), new_group, TRUE,
+          groups_change_group_cb, NULL);
+    }
+
+  if (old_group != NULL && action == GDK_ACTION_MOVE)
+    {
+      folks_groups_change_group (FOLKS_GROUPS (individual), old_group,
+          FALSE, groups_change_group_cb, NULL);
+    }
+}
+
+static gboolean
+individual_view_persona_drag_received (GtkWidget *self,
+    GdkDragContext *context,
+    GtkTreeModel *model,
+    GtkTreePath *path,
+    GtkSelectionData *selection)
+{
+  EmpathyIndividualViewPriv *priv;
+  EmpathyIndividualManager *manager = NULL;
+  FolksIndividual *individual = NULL;
+  FolksPersona *persona = NULL;
+  const gchar *persona_uid;
+  GList *individuals, *l;
+  gboolean retval = FALSE;
+
+  priv = GET_PRIV (self);
+
+  persona_uid = (const gchar *) gtk_selection_data_get_data (selection);
+
+  /* FIXME: This is slow, but the only way to find the Persona we're having
+   * dropped on us. */
+  manager = empathy_individual_manager_dup_singleton ();
+  individuals = empathy_individual_manager_get_members (manager);
+
+  for (l = individuals; l != NULL; l = l->next)
+    {
+      GList *personas, *p;
+
+      personas = folks_individual_get_personas (FOLKS_INDIVIDUAL (l->data));
+
+      for (p = personas; p != NULL; p = p->next)
+        {
+          if (!tp_strdiff (folks_persona_get_uid (FOLKS_PERSONA (p->data)),
+              persona_uid))
+            {
+              persona = g_object_ref (p->data);
+              individual = g_object_ref (l->data);
+              goto got_persona;
+            }
+        }
+    }
+
+got_persona:
+  g_list_free (individuals);
+
+  if (persona == NULL || individual == NULL)
+    {
+      DEBUG ("Failed to find drag event persona with UID '%s'", persona_uid);
+    }
+  else
+    {
+      /* Emit a signal notifying of the drag. We change the Individual's groups in
+       * the default signal handler. */
+      g_signal_emit (self, signals[DRAG_PERSONA_RECEIVED], 0,
+          gdk_drag_context_get_selected_action (context), persona, individual,
+          &retval);
+    }
+
+  tp_clear_object (&manager);
+  tp_clear_object (&persona);
+  tp_clear_object (&individual);
+
+  return retval;
 }
 
 static gboolean
@@ -436,13 +511,17 @@ individual_view_drag_data_received (GtkWidget *view,
     {
       success = FALSE;
     }
-  else if (info == DND_DRAG_TYPE_INDIVIDUAL_ID
-      || info == DND_DRAG_TYPE_STRING)
+  else if (info == DND_DRAG_TYPE_INDIVIDUAL_ID)
     {
-      success = individual_view_contact_drag_received (view,
+      success = individual_view_individual_drag_received (view,
           context, model, path, selection);
     }
-  else if (info == DND_DRAG_TYPE_URI_LIST)
+  else if (info == DND_DRAG_TYPE_PERSONA_ID)
+    {
+      success = individual_view_persona_drag_received (view, context, model,
+          path, selection);
+    }
+  else if (info == DND_DRAG_TYPE_URI_LIST || info == DND_DRAG_TYPE_STRING)
     {
       success = individual_view_file_drag_received (view,
           context, model, path, selection);
@@ -511,17 +590,73 @@ individual_view_drag_motion (GtkWidget *widget,
       gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0);
       return FALSE;
     }
-  target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
+  target = gtk_drag_dest_find_target (widget, context, NULL);
   gtk_tree_model_get_iter (model, &iter, path);
 
-  if (target == GDK_NONE)
+  if (target == drag_atoms_dest[DND_DRAG_TYPE_URI_LIST] ||
+      target == drag_atoms_dest[DND_DRAG_TYPE_STRING])
+    {
+      /* This is a file drag, and it can only be dropped on contacts,
+       * not groups.
+       * If we don't have FEATURE_FILE_DROP, disallow the drop completely,
+       * even if we have a valid target. */
+      FolksIndividual *individual = NULL;
+      EmpathyCapabilities caps = EMPATHY_CAPABILITIES_NONE;
+
+      if (priv->view_features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_FILE_DROP)
+        {
+          gtk_tree_model_get (model, &iter,
+              EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
+              -1);
+        }
+
+      if (individual != NULL)
+        {
+          EmpathyContact *contact = NULL;
+
+          contact = empathy_contact_dup_from_folks_individual (individual);
+          caps = empathy_contact_get_capabilities (contact);
+
+          tp_clear_object (&contact);
+        }
+
+      if (individual != NULL &&
+          folks_individual_is_online (individual) &&
+          (caps & EMPATHY_CAPABILITIES_FT))
+        {
+          gdk_drag_status (context, GDK_ACTION_COPY, time_);
+          gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
+              path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
+        }
+      else
+        {
+          gdk_drag_status (context, 0, time_);
+          gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0);
+          retval = FALSE;
+        }
+
+      if (individual != NULL)
+        g_object_unref (individual);
+    }
+  else if ((target == drag_atoms_dest[DND_DRAG_TYPE_INDIVIDUAL_ID] &&
+      (priv->view_features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_CHANGE ||
+       priv->drag_row == NULL)) ||
+      (target == drag_atoms_dest[DND_DRAG_TYPE_PERSONA_ID] &&
+       priv->view_features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_PERSONA_DROP))
     {
-      /* If target == GDK_NONE, then we don't have a target that can be
-         dropped on a contact.  This means a contact drag.  If we're
-         pointing to a group, highlight it.  Otherwise, if the contact
-         we're pointing to is in a group, highlight that.  Otherwise,
+      /* If target != GDK_NONE, then we have a contact (individual or persona)
+         drag.  If we're pointing to a group, highlight it.  Otherwise, if the
+         contact we're pointing to is in a group, highlight that.  Otherwise,
          set the drag position to before the first row for a drag into
          the "non-group" at the top.
+         If it's an Individual:
+           We only highlight things if the contact is from a different
+           Individual view, or if this Individual view has
+           FEATURE_GROUPS_CHANGE. This prevents highlighting in Individual views
+           which don't have FEATURE_GROUPS_CHANGE, but do have
+           FEATURE_INDIVIDUAL_DRAG and FEATURE_INDIVIDUAL_DROP.
+         If it's a Persona:
+           We only highlight things if we have FEATURE_PERSONA_DROP.
        */
       GtkTreeIter group_iter;
       gboolean is_group;
@@ -554,44 +689,6 @@ individual_view_drag_motion (GtkWidget *widget,
               group_path, GTK_TREE_VIEW_DROP_BEFORE);
         }
     }
-  else
-    {
-      /* This is a file drag, and it can only be dropped on contacts,
-         not groups.
-       */
-      FolksIndividual *individual;
-      EmpathyCapabilities caps = EMPATHY_CAPABILITIES_NONE;
-
-      gtk_tree_model_get (model, &iter,
-          EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, -1);
-      if (individual != NULL)
-        {
-          EmpathyContact *contact = NULL;
-
-          contact = empathy_contact_dup_from_folks_individual (individual);
-          caps = empathy_contact_get_capabilities (contact);
-
-          tp_clear_object (&contact);
-        }
-
-      if (individual != NULL &&
-          folks_individual_is_online (individual) &&
-          (caps & EMPATHY_CAPABILITIES_FT))
-        {
-          gdk_drag_status (context, GDK_ACTION_COPY, time_);
-          gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
-              path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
-        }
-      else
-        {
-          gdk_drag_status (context, 0, time_);
-          gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0);
-          retval = FALSE;
-        }
-
-      if (individual != NULL)
-        g_object_unref (individual);
-    }
 
   if (!is_different && !cleanup)
     return retval;
@@ -807,7 +904,7 @@ individual_view_row_activated (GtkTreeView *view,
   GtkTreeModel *model;
   GtkTreeIter iter;
 
-  if (!(priv->individual_features & EMPATHY_CONTACT_FEATURE_CHAT))
+  if (!(priv->individual_features & EMPATHY_INDIVIDUAL_FEATURE_CHAT))
     return;
 
   model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
@@ -1731,10 +1828,10 @@ individual_view_set_view_features (EmpathyIndividualView *view,
      is enabled).
    */
   gtk_tree_view_set_reorderable (GTK_TREE_VIEW (view),
-      (features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_DRAG));
+      (features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DRAG));
 
   /* Update DnD source/dest */
-  if (features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_DRAG)
+  if (features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DRAG)
     {
       gtk_drag_source_set (GTK_WIDGET (view),
           GDK_BUTTON1_MASK,
@@ -1748,7 +1845,7 @@ individual_view_set_view_features (EmpathyIndividualView *view,
 
     }
 
-  if (features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_DROP)
+  if (features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DROP)
     {
       gtk_drag_dest_set (GTK_WIDGET (view),
           GTK_DEST_DEFAULT_ALL,
@@ -1763,7 +1860,7 @@ individual_view_set_view_features (EmpathyIndividualView *view,
 
   /* Update has-tooltip */
   has_tooltip =
-      (features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_TOOLTIP) != 0;
+      (features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_TOOLTIP) != 0;
   gtk_widget_set_has_tooltip (GTK_WIDGET (view), has_tooltip);
 }
 
@@ -1776,7 +1873,6 @@ individual_view_dispose (GObject *object)
   tp_clear_object (&priv->store);
   tp_clear_object (&priv->filter);
   tp_clear_pointer (&priv->tooltip_widget, gtk_widget_destroy);
-  tp_clear_pointer (&priv->file_targets, gtk_target_list_unref);
 
   empathy_individual_view_set_live_search (view, NULL);
 
@@ -1878,14 +1974,26 @@ empathy_individual_view_class_init (EmpathyIndividualViewClass *klass)
    * won't be called. */
   tree_view_class->row_activated = individual_view_row_activated;
 
-  signals[DRAG_CONTACT_RECEIVED] =
-      g_signal_new ("drag-contact-received",
+  klass->drag_individual_received = real_drag_individual_received_cb;
+
+  signals[DRAG_INDIVIDUAL_RECEIVED] =
+      g_signal_new ("drag-individual-received",
       G_OBJECT_CLASS_TYPE (klass),
       G_SIGNAL_RUN_LAST,
-      0,
+      G_STRUCT_OFFSET (EmpathyIndividualViewClass, drag_individual_received),
       NULL, NULL,
-      _empathy_gtk_marshal_VOID__OBJECT_STRING_STRING,
-      G_TYPE_NONE, 3, EMPATHY_TYPE_CONTACT, G_TYPE_STRING, G_TYPE_STRING);
+      _empathy_gtk_marshal_VOID__UINT_OBJECT_STRING_STRING,
+      G_TYPE_NONE, 4, G_TYPE_UINT, FOLKS_TYPE_INDIVIDUAL,
+      G_TYPE_STRING, G_TYPE_STRING);
+
+  signals[DRAG_PERSONA_RECEIVED] =
+      g_signal_new ("drag-persona-received",
+      G_OBJECT_CLASS_TYPE (klass),
+      G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (EmpathyIndividualViewClass, drag_persona_received),
+      NULL, NULL,
+      _empathy_gtk_marshal_BOOLEAN__UINT_OBJECT_OBJECT,
+      G_TYPE_BOOLEAN, 3, G_TYPE_UINT, FOLKS_TYPE_PERSONA, FOLKS_TYPE_INDIVIDUAL);
 
   g_object_class_install_property (object_class,
       PROP_STORE,
@@ -1904,10 +2012,10 @@ empathy_individual_view_class_init (EmpathyIndividualViewClass *klass)
   g_object_class_install_property (object_class,
       PROP_INDIVIDUAL_FEATURES,
       g_param_spec_flags ("individual-features",
-          "Features of the contact menu",
+          "Features of the individual menu",
           "Flags for all enabled features for the menu",
           EMPATHY_TYPE_INDIVIDUAL_FEATURE_FLAGS,
-          EMPATHY_CONTACT_FEATURE_NONE, G_PARAM_READWRITE));
+          EMPATHY_INDIVIDUAL_FEATURE_NONE, G_PARAM_READWRITE));
   g_object_class_install_property (object_class,
       PROP_SHOW_OFFLINE,
       g_param_spec_boolean ("show-offline",
@@ -1934,10 +2042,6 @@ empathy_individual_view_init (EmpathyIndividualView *view)
   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view),
       empathy_individual_store_row_separator_func, NULL, NULL);
 
-  /* Set up drag target lists. */
-  priv->file_targets = gtk_target_list_new (drag_types_dest_file,
-      G_N_ELEMENTS (drag_types_dest_file));
-
   /* Connect to tree view signals rather than override. */
   g_signal_connect (view, "button-press-event",
       G_CALLBACK (individual_view_button_press_event_cb), NULL);
@@ -2213,7 +2317,7 @@ empathy_individual_view_get_individual_menu (EmpathyIndividualView *view)
 
   /* Remove contact */
   if (priv->view_features &
-      EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_REMOVE &&
+      EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_REMOVE &&
       flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_REMOVE)
     {
 
index 003ff2ab3760609c36a3acf9f1e791783cf3c077..8a250bf20e1248533c8a9b2805066c8b983d81c5 100644 (file)
@@ -53,11 +53,16 @@ typedef enum
   EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_SAVE = 1 << 0,
   EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_RENAME = 1 << 1,
   EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_REMOVE = 1 << 2,
-  EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_REMOVE = 1 << 3,
-  EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_DROP = 1 << 4,
-  EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_DRAG = 1 << 5,
-  EMPATHY_INDIVIDUAL_VIEW_FEATURE_CONTACT_TOOLTIP = 1 << 6,
-  EMPATHY_INDIVIDUAL_VIEW_FEATURE_ALL = (1 << 7) - 1,
+  /* NOTE: For this to behave as expected, FEATURE_INDIVIDUAL_DRAG and
+   * FEATURE_INDIVIDUAL_DROP should also be specified. */
+  EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_CHANGE = 1 << 3,
+  EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_REMOVE = 1 << 4,
+  EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DROP = 1 << 5,
+  EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DRAG = 1 << 6,
+  EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_TOOLTIP = 1 << 7,
+  EMPATHY_INDIVIDUAL_VIEW_FEATURE_PERSONA_DROP = 1 << 8,
+  EMPATHY_INDIVIDUAL_VIEW_FEATURE_FILE_DROP = 1 << 9,
+  EMPATHY_INDIVIDUAL_VIEW_FEATURE_ALL = (1 << 10) - 1,
 } EmpathyIndividualViewFeatureFlags;
 
 struct _EmpathyIndividualView
@@ -69,6 +74,17 @@ struct _EmpathyIndividualView
 struct _EmpathyIndividualViewClass
 {
   GtkTreeViewClass parent_class;
+
+  void (* drag_individual_received) (EmpathyIndividualView *self,
+      GdkDragAction action,
+      FolksIndividual *individual,
+      const gchar *new_group,
+      const gchar *old_group);
+
+  void (* drag_persona_received) (EmpathyIndividualView *self,
+      GdkDragAction action,
+      FolksPersona *persona,
+      FolksIndividual *individual);
 };
 
 GType empathy_individual_view_get_type (void) G_GNUC_CONST;
index 04777b1b6c2e5d1b41b92c9c0e7b308fec5df0b3..adfe6be5ce1d1ca387f58b3a88e02bab9f94037e 100644 (file)
@@ -37,6 +37,7 @@
 #include <folks/folks.h>
 #include <folks/folks-telepathy.h>
 
+#include <libempathy/empathy-individual-manager.h>
 #include <libempathy/empathy-utils.h>
 
 #include "empathy-persona-view.h"
@@ -44,6 +45,8 @@
 #include "empathy-images.h"
 #include "empathy-cell-renderer-text.h"
 #include "empathy-cell-renderer-activatable.h"
+#include "empathy-gtk-enum-types.h"
+#include "empathy-gtk-marshal.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
 #include <libempathy/empathy-debug.h>
@@ -69,6 +72,7 @@ typedef struct
   GtkTreeModelFilter *filter;
   GtkWidget *tooltip_widget;
   gboolean show_offline;
+  EmpathyPersonaViewFeatureFlags features;
 } EmpathyPersonaViewPriv;
 
 enum
@@ -76,8 +80,42 @@ enum
   PROP_0,
   PROP_MODEL,
   PROP_SHOW_OFFLINE,
+  PROP_FEATURES,
 };
 
+enum DndDragType
+{
+  DND_DRAG_TYPE_INDIVIDUAL_ID,
+  DND_DRAG_TYPE_PERSONA_ID,
+  DND_DRAG_TYPE_STRING,
+};
+
+#define DRAG_TYPE(T,I) \
+  { (gchar *) T, 0, I }
+
+static const GtkTargetEntry drag_types_dest[] = {
+  DRAG_TYPE ("text/individual-id", DND_DRAG_TYPE_INDIVIDUAL_ID),
+  DRAG_TYPE ("text/plain", DND_DRAG_TYPE_STRING),
+  DRAG_TYPE ("STRING", DND_DRAG_TYPE_STRING),
+};
+
+static const GtkTargetEntry drag_types_source[] = {
+  DRAG_TYPE ("text/persona-id", DND_DRAG_TYPE_PERSONA_ID),
+};
+
+#undef DRAG_TYPE
+
+static GdkAtom drag_atoms_dest[G_N_ELEMENTS (drag_types_dest)];
+static GdkAtom drag_atoms_source[G_N_ELEMENTS (drag_types_source)];
+
+enum
+{
+  DRAG_INDIVIDUAL_RECEIVED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
 G_DEFINE_TYPE (EmpathyPersonaView, empathy_persona_view, GTK_TYPE_TREE_VIEW);
 
 static gboolean
@@ -311,6 +349,184 @@ text_cell_data_func (GtkTreeViewColumn *tree_column,
   cell_set_background (self, cell, is_active);
 }
 
+static gboolean
+individual_drag_received (EmpathyPersonaView *self,
+    GdkDragContext *context,
+    GtkSelectionData *selection)
+{
+  EmpathyPersonaViewPriv *priv;
+  EmpathyIndividualManager *manager = NULL;
+  FolksIndividual *individual;
+  const gchar *individual_id;
+  gboolean success = FALSE;
+
+  priv = GET_PRIV (self);
+
+  individual_id = (const gchar *) gtk_selection_data_get_data (selection);
+  manager = empathy_individual_manager_dup_singleton ();
+  individual = empathy_individual_manager_lookup_member (manager,
+      individual_id);
+
+  if (individual == NULL)
+    {
+      DEBUG ("Failed to find drag event individual with ID '%s'",
+          individual_id);
+      g_object_unref (manager);
+      return FALSE;
+    }
+
+  /* Emit a signal notifying of the drag. */
+  g_signal_emit (self, signals[DRAG_INDIVIDUAL_RECEIVED], 0,
+      gdk_drag_context_get_selected_action (context), individual, &success);
+
+  g_object_unref (manager);
+
+  return success;
+}
+
+static void
+drag_data_received (GtkWidget *widget,
+    GdkDragContext *context,
+    gint x,
+    gint y,
+    GtkSelectionData *selection,
+    guint info,
+    guint time_)
+{
+  EmpathyPersonaView *self = EMPATHY_PERSONA_VIEW (widget);
+  gboolean success = TRUE;
+
+  if (info == DND_DRAG_TYPE_INDIVIDUAL_ID || info == DND_DRAG_TYPE_STRING)
+    success = individual_drag_received (self, context, selection);
+
+  gtk_drag_finish (context, success, FALSE, GDK_CURRENT_TIME);
+}
+
+static gboolean
+drag_motion (GtkWidget *widget,
+    GdkDragContext *context,
+    gint x,
+    gint y,
+    guint time_)
+{
+  EmpathyPersonaView *self = EMPATHY_PERSONA_VIEW (widget);
+  EmpathyPersonaViewPriv *priv;
+  GdkAtom target;
+
+  priv = GET_PRIV (self);
+
+  target = gtk_drag_dest_find_target (GTK_WIDGET (self), context, NULL);
+
+  if (target == drag_atoms_dest[DND_DRAG_TYPE_INDIVIDUAL_ID])
+    {
+      GtkTreePath *path;
+
+      /* FIXME: It doesn't make sense for us to highlight a specific row or
+       * position to drop an Individual in, so just highlight the entire
+       * widget.
+       * Since I can't find a way to do this, just highlight the first possible
+       * position in the tree. */
+      gdk_drag_status (context, gdk_drag_context_get_suggested_action (context),
+          time_);
+
+      path = gtk_tree_path_new_first ();
+      gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (self), path,
+          GTK_TREE_VIEW_DROP_BEFORE);
+      gtk_tree_path_free (path);
+
+      return TRUE;
+    }
+
+  /* Unknown or unhandled drag target */
+  gdk_drag_status (context, GDK_ACTION_DEFAULT, time_);
+  gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (self), NULL, 0);
+
+  return FALSE;
+}
+
+static void
+drag_data_get (GtkWidget *widget,
+    GdkDragContext *context,
+    GtkSelectionData *selection,
+    guint info,
+    guint time_)
+{
+  EmpathyPersonaView *self = EMPATHY_PERSONA_VIEW (widget);
+  EmpathyPersonaViewPriv *priv;
+  FolksPersona *persona;
+  const gchar *persona_uid;
+
+  if (info != DND_DRAG_TYPE_PERSONA_ID)
+    return;
+
+  priv = GET_PRIV (self);
+
+  persona = empathy_persona_view_dup_selected (self);
+  if (persona == NULL)
+    return;
+
+  persona_uid = folks_persona_get_uid (persona);
+  gtk_selection_data_set (selection, drag_atoms_source[info], 8,
+      (guchar *) persona_uid, strlen (persona_uid) + 1);
+
+  g_object_unref (persona);
+}
+
+static gboolean
+drag_drop (GtkWidget *widget,
+    GdkDragContext *drag_context,
+    gint x,
+    gint y,
+    guint time_)
+{
+  return FALSE;
+}
+
+static void
+set_features (EmpathyPersonaView *self,
+    EmpathyPersonaViewFeatureFlags features)
+{
+  EmpathyPersonaViewPriv *priv = GET_PRIV (self);
+
+  priv->features = features;
+
+  /* Setting reorderable is a hack that gets us row previews as drag icons
+     for free.  We override all the drag handlers.  It's tricky to get the
+     position of the drag icon right in drag_begin.  GtkTreeView has special
+     voodoo for it, so we let it do the voodoo that he do (but only if dragging
+     is enabled). */
+  gtk_tree_view_set_reorderable (GTK_TREE_VIEW (self),
+      (features & EMPATHY_PERSONA_VIEW_FEATURE_PERSONA_DRAG));
+
+  /* Update DnD source/dest */
+  if (features & EMPATHY_PERSONA_VIEW_FEATURE_PERSONA_DRAG)
+    {
+      gtk_drag_source_set (GTK_WIDGET (self),
+          GDK_BUTTON1_MASK,
+          drag_types_source,
+          G_N_ELEMENTS (drag_types_source),
+          GDK_ACTION_MOVE | GDK_ACTION_COPY);
+    }
+  else
+    {
+      gtk_drag_source_unset (GTK_WIDGET (self));
+    }
+
+  if (features & EMPATHY_PERSONA_VIEW_FEATURE_PERSONA_DROP)
+    {
+      gtk_drag_dest_set (GTK_WIDGET (self),
+          GTK_DEST_DEFAULT_ALL,
+          drag_types_dest,
+          G_N_ELEMENTS (drag_types_dest), GDK_ACTION_MOVE | GDK_ACTION_COPY);
+    }
+  else
+    {
+      gtk_drag_dest_unset (GTK_WIDGET (self));
+    }
+
+  g_object_notify (G_OBJECT (self), "features");
+}
+
 static void
 empathy_persona_view_init (EmpathyPersonaView *self)
 {
@@ -329,6 +545,7 @@ constructed (GObject *object)
   EmpathyPersonaView *self = EMPATHY_PERSONA_VIEW (object);
   GtkCellRenderer *cell;
   GtkTreeViewColumn *col;
+  guint i;
 
   /* Set up view */
   g_object_set (self,
@@ -393,6 +610,13 @@ constructed (GObject *object)
 
   /* Actually add the column now we have added all cell renderers */
   gtk_tree_view_append_column (GTK_TREE_VIEW (self), col);
+
+  /* Drag & Drop. */
+  for (i = 0; i < G_N_ELEMENTS (drag_types_dest); ++i)
+    drag_atoms_dest[i] = gdk_atom_intern (drag_types_dest[i].target, FALSE);
+
+  for (i = 0; i < G_N_ELEMENTS (drag_types_source); ++i)
+    drag_atoms_source[i] = gdk_atom_intern (drag_types_source[i].target, FALSE);
 }
 
 static void
@@ -411,6 +635,9 @@ get_property (GObject *object,
       case PROP_SHOW_OFFLINE:
         g_value_set_boolean (value, priv->show_offline);
         break;
+      case PROP_FEATURES:
+        g_value_set_flags (value, priv->features);
+        break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
         break;
@@ -434,6 +661,9 @@ set_property (GObject *object,
         empathy_persona_view_set_show_offline (self,
             g_value_get_boolean (value));
         break;
+      case PROP_FEATURES:
+        set_features (self, g_value_get_flags (value));
+        break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
         break;
@@ -459,12 +689,27 @@ static void
 empathy_persona_view_class_init (EmpathyPersonaViewClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->constructed = constructed;
   object_class->dispose = dispose;
   object_class->get_property = get_property;
   object_class->set_property = set_property;
 
+  widget_class->drag_data_received = drag_data_received;
+  widget_class->drag_drop = drag_drop;
+  widget_class->drag_data_get = drag_data_get;
+  widget_class->drag_motion = drag_motion;
+
+  signals[DRAG_INDIVIDUAL_RECEIVED] =
+      g_signal_new ("drag-individual-received",
+      G_OBJECT_CLASS_TYPE (klass),
+      G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (EmpathyPersonaViewClass, drag_individual_received),
+      NULL, NULL,
+      _empathy_gtk_marshal_BOOLEAN__UINT_OBJECT,
+      G_TYPE_BOOLEAN, 2, G_TYPE_UINT, FOLKS_TYPE_INDIVIDUAL);
+
   /* We override the "model" property so that we can wrap it in a
    * GtkTreeModelFilter for showing/hiding offline personas. */
   g_object_class_override_property (object_class, PROP_MODEL, "model");
@@ -481,12 +726,27 @@ empathy_persona_view_class_init (EmpathyPersonaViewClass *klass)
           FALSE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * EmpathyPersonaStore:features:
+   *
+   * Features of the view, such as whether drag and drop is enabled.
+   */
+  g_object_class_install_property (object_class, PROP_FEATURES,
+      g_param_spec_flags ("features",
+          "Features",
+          "Flags for all enabled features.",
+          EMPATHY_TYPE_PERSONA_VIEW_FEATURE_FLAGS,
+          EMPATHY_PERSONA_VIEW_FEATURE_NONE,
+          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   g_type_class_add_private (object_class, sizeof (EmpathyPersonaViewPriv));
 }
 
 /**
  * empathy_persona_view_new:
  * @store: an #EmpathyPersonaStore
+ * @features: a set of flags specifying the view's functionality, or
+ * %EMPATHY_PERSONA_VIEW_FEATURE_NONE
  *
  * Create a new #EmpathyPersonaView displaying the personas in
  * #EmpathyPersonaStore.
@@ -494,11 +754,15 @@ empathy_persona_view_class_init (EmpathyPersonaViewClass *klass)
  * Return value: a new #EmpathyPersonaView
  */
 EmpathyPersonaView *
-empathy_persona_view_new (EmpathyPersonaStore *store)
+empathy_persona_view_new (EmpathyPersonaStore *store,
+    EmpathyPersonaViewFeatureFlags features)
 {
   g_return_val_if_fail (EMPATHY_IS_PERSONA_STORE (store), NULL);
 
-  return g_object_new (EMPATHY_TYPE_PERSONA_VIEW, "model", store, NULL);
+  return g_object_new (EMPATHY_TYPE_PERSONA_VIEW,
+      "model", store,
+      "features", features,
+      NULL);
 }
 
 /**
index 11fe039eb81fed122e80102336a7a29801254503..0a6317c6b0e88562e271050932e8c9e357c10065 100644 (file)
 
 G_BEGIN_DECLS
 
+typedef enum
+{
+  EMPATHY_PERSONA_VIEW_FEATURE_NONE = 0,
+  EMPATHY_PERSONA_VIEW_FEATURE_PERSONA_DRAG = 1 << 0,
+  EMPATHY_PERSONA_VIEW_FEATURE_PERSONA_DROP = 1 << 1,
+  EMPATHY_PERSONA_VIEW_FEATURE_ALL = (1 << 2) - 1,
+} EmpathyPersonaViewFeatureFlags;
+
 #define EMPATHY_TYPE_PERSONA_VIEW (empathy_persona_view_get_type ())
 #define EMPATHY_PERSONA_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), \
     EMPATHY_TYPE_PERSONA_VIEW, EmpathyPersonaView))
@@ -57,11 +65,16 @@ typedef struct
 typedef struct
 {
   GtkTreeViewClass parent_class;
+
+  void (* drag_individual_received) (EmpathyPersonaView *self,
+      GdkDragAction action,
+      FolksIndividual *individual);
 } EmpathyPersonaViewClass;
 
 GType empathy_persona_view_get_type (void) G_GNUC_CONST;
 
-EmpathyPersonaView *empathy_persona_view_new (EmpathyPersonaStore *store);
+EmpathyPersonaView *empathy_persona_view_new (EmpathyPersonaStore *store,
+    EmpathyPersonaViewFeatureFlags features);
 
 FolksPersona *empathy_persona_view_dup_selected (EmpathyPersonaView *self);
 
index 4d3eabb2206f45bfed499da21b768b1a8acafe9e..1e76cbf834b0adbe7d0a7ef510dabc7416aa992d 100644 (file)
@@ -171,7 +171,7 @@ aggregator_individuals_changed_cb (FolksIndividualAggregator *aggregator,
     EmpathyIndividualManager *self)
 {
   EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
-  GList *l, *added_filtered = NULL, *removed_filtered = NULL;
+  GList *l, *added_filtered = NULL;
 
   /* Filter the individuals for ones which contain EmpathyContacts */
   for (l = added; l; l = l->next)
@@ -197,26 +197,21 @@ aggregator_individuals_changed_cb (FolksIndividualAggregator *aggregator,
 
       if (g_hash_table_lookup (priv->individuals,
           folks_individual_get_id (ind)) != NULL)
-        {
-          removed_filtered = g_list_prepend (removed_filtered, ind);
-          remove_individual (self, ind);
-        }
+        remove_individual (self, ind);
     }
 
   /* Bail if we have no individuals left */
-  if (added_filtered == NULL && removed_filtered == NULL)
+  if (added_filtered == NULL && removed == NULL)
     return;
 
   added_filtered = g_list_reverse (added_filtered);
-  removed_filtered = g_list_reverse (removed_filtered);
 
   g_signal_emit (self, signals[MEMBERS_CHANGED], 0, message,
-      added_filtered, removed_filtered,
+      added_filtered, removed,
       tp_chanel_group_change_reason_from_folks_groups_change_reason (reason),
       TRUE);
 
   g_list_free (added_filtered);
-  g_list_free (removed_filtered);
 }
 
 static void
index 5f9767639c47d08b75afb7ba3608001e3b31d659..fa6f1531007e385ae1b8fe5981481b71ca617088 100644 (file)
@@ -1680,9 +1680,12 @@ empathy_main_window_init (EmpathyMainWindow *window)
                        individual_manager);
        g_object_unref (individual_manager);
 
+       /* For the moment, we disallow Persona drops onto the main contact list (e.g. from things such as
+        * the EmpathyPersonaView in the linking dialogue). No code is hooked up to do anything on a Persona
+        * drop, so allowing them would achieve nothing except confusion. */
        priv->individual_view = empathy_individual_view_new (
                        priv->individual_store,
-                       EMPATHY_INDIVIDUAL_VIEW_FEATURE_ALL,
+                       EMPATHY_INDIVIDUAL_VIEW_FEATURE_ALL ^ EMPATHY_INDIVIDUAL_VIEW_FEATURE_PERSONA_DROP,
                        EMPATHY_INDIVIDUAL_FEATURE_ALL);
 
        priv->butterfly_log_migration_members_changed_id = g_signal_connect (