]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-individual-view.c
Merge branch 'sasl'
[empathy.git] / libempathy-gtk / empathy-individual-view.c
index 9ef9c103a615a62cc32a3558c6938c2073b1d84f..39dc3b34b98c2b3fc1325ed6bc74662bb8bce30f 100644 (file)
@@ -48,6 +48,7 @@
 #include "empathy-individual-menu.h"
 #include "empathy-individual-store.h"
 #include "empathy-images.h"
+#include "empathy-linking-dialog.h"
 #include "empathy-cell-renderer-expander.h"
 #include "empathy-cell-renderer-text.h"
 #include "empathy-cell-renderer-activatable.h"
@@ -72,6 +73,7 @@ typedef struct
   GtkWidget *tooltip_widget;
 
   gboolean show_offline;
+  gboolean show_untrusted;
 
   GtkTreeModelFilter *filter;
   GtkWidget *search_widget;
@@ -79,6 +81,12 @@ typedef struct
   guint expand_groups_idle_handler;
   /* owned string (group name) -> bool (whether to expand/contract) */
   GHashTable *expand_groups;
+
+  /* Auto scroll */
+  guint auto_scroll_timeout_id;
+  /* Distance between mouse pointer and the nearby border. Negative when
+     scrolling updards.*/
+  gint distance;
 } EmpathyIndividualViewPriv;
 
 typedef struct
@@ -102,6 +110,7 @@ enum
   PROP_VIEW_FEATURES,
   PROP_INDIVIDUAL_FEATURES,
   PROP_SHOW_OFFLINE,
+  PROP_SHOW_UNTRUSTED,
 };
 
 /* TODO: re-add DRAG_TYPE_CONTACT_ID, for the case that we're dragging around
@@ -205,7 +214,8 @@ individual_view_query_tooltip_cb (EmpathyIndividualView *view,
     {
       priv->tooltip_widget = empathy_individual_widget_new (individual,
           EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP |
-          EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION);
+          EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION |
+          EMPATHY_INDIVIDUAL_WIDGET_SHOW_CLIENT_TYPES);
       gtk_container_set_border_width (GTK_CONTAINER (priv->tooltip_widget), 8);
       g_object_ref (priv->tooltip_widget);
       g_signal_connect (priv->tooltip_widget, "destroy",
@@ -233,10 +243,10 @@ groups_change_group_cb (GObject *source,
     GAsyncResult *result,
     gpointer user_data)
 {
-  FolksGroups *groups = FOLKS_GROUPS (source);
+  FolksGroupable *groupable = FOLKS_GROUPABLE (source);
   GError *error = NULL;
 
-  folks_groups_change_group_finish (groups, result, &error);
+  folks_groupable_change_group_finish (groupable, result, &error);
   if (error != NULL)
     {
       g_warning ("failed to change group: %s", error->message);
@@ -382,13 +392,13 @@ real_drag_individual_received_cb (EmpathyIndividualView *self,
 
   if (new_group != NULL)
     {
-      folks_groups_change_group (FOLKS_GROUPS (individual), new_group, TRUE,
+      folks_groupable_change_group (FOLKS_GROUPABLE (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,
+      folks_groupable_change_group (FOLKS_GROUPABLE (individual), old_group,
           FALSE, groups_change_group_cb, NULL);
     }
 }
@@ -511,8 +521,7 @@ 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_individual_drag_received (view,
           context, model, path, selection);
@@ -522,7 +531,7 @@ individual_view_drag_data_received (GtkWidget *view,
       success = individual_view_persona_drag_received (view, context, model,
           path, selection);
     }
-  else if (info == DND_DRAG_TYPE_URI_LIST)
+  else if (info == DND_DRAG_TYPE_URI_LIST || info == DND_DRAG_TYPE_STRING)
     {
       success = individual_view_file_drag_received (view,
           context, model, path, selection);
@@ -547,6 +556,34 @@ individual_view_drag_motion_cb (DragMotionData *data)
   return FALSE;
 }
 
+/* Minimum distance between the mouse pointer and a horizontal border when we
+   start auto scrolling. */
+#define AUTO_SCROLL_MARGIN_SIZE 20
+/* How far to scroll per one tick. */
+#define AUTO_SCROLL_PITCH       10
+
+static gboolean
+individual_view_auto_scroll_cb (EmpathyIndividualView *self)
+{
+       EmpathyIndividualViewPriv *priv = GET_PRIV (self);
+       GtkAdjustment         *adj;
+       gdouble                new_value;
+
+       adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self));
+
+       if (priv->distance < 0)
+               new_value = gtk_adjustment_get_value (adj) - AUTO_SCROLL_PITCH;
+       else
+               new_value = gtk_adjustment_get_value (adj) + AUTO_SCROLL_PITCH;
+
+       new_value = CLAMP (new_value, gtk_adjustment_get_lower (adj),
+               gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
+
+       gtk_adjustment_set_value (adj, new_value);
+
+       return TRUE;
+}
+
 static gboolean
 individual_view_drag_motion (GtkWidget *widget,
     GdkDragContext *context,
@@ -564,10 +601,32 @@ individual_view_drag_motion (GtkWidget *widget,
   gboolean is_different = FALSE;
   gboolean cleanup = TRUE;
   gboolean retval = TRUE;
+  GtkAllocation allocation;
 
   priv = GET_PRIV (EMPATHY_INDIVIDUAL_VIEW (widget));
   model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
 
+
+  if (priv->auto_scroll_timeout_id != 0)
+    {
+      g_source_remove (priv->auto_scroll_timeout_id);
+      priv->auto_scroll_timeout_id = 0;
+    }
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  if (y < AUTO_SCROLL_MARGIN_SIZE ||
+      y > (allocation.height - AUTO_SCROLL_MARGIN_SIZE))
+    {
+      if (y < AUTO_SCROLL_MARGIN_SIZE)
+        priv->distance = MIN (-y, -1);
+      else
+        priv->distance = MAX (allocation.height - y, 1);
+
+      priv->auto_scroll_timeout_id = g_timeout_add (10 * ABS (priv->distance),
+          (GSourceFunc) individual_view_auto_scroll_cb, widget);
+    }
+
   is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
       x, y, &path, NULL, NULL, NULL);
 
@@ -622,7 +681,7 @@ individual_view_drag_motion (GtkWidget *widget,
         }
 
       if (individual != NULL &&
-          folks_individual_is_online (individual) &&
+          folks_presence_is_online (FOLKS_PRESENCE (individual)) &&
           (caps & EMPATHY_CAPABILITIES_FT))
         {
           gdk_drag_status (context, GDK_ACTION_COPY, time_);
@@ -639,8 +698,7 @@ individual_view_drag_motion (GtkWidget *widget,
       if (individual != NULL)
         g_object_unref (individual);
     }
-  else if (((target == drag_atoms_dest[DND_DRAG_TYPE_STRING] ||
-       target == drag_atoms_dest[DND_DRAG_TYPE_INDIVIDUAL_ID]) &&
+  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] &&
@@ -842,7 +900,6 @@ individual_view_popup_menu_idle_cb (gpointer user_data)
 
   if (menu != NULL)
     {
-      g_signal_connect (menu, "deactivate", G_CALLBACK (gtk_menu_detach), NULL);
       gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (data->view),
           NULL);
       gtk_widget_show (menu);
@@ -881,7 +938,7 @@ individual_view_key_press_event_cb (EmpathyIndividualView *view,
     GdkEventKey *event,
     gpointer user_data)
 {
-  if (event->keyval == GDK_Menu)
+  if (event->keyval == GDK_KEY_Menu)
     {
       MenuPopupData *data;
 
@@ -902,7 +959,7 @@ individual_view_row_activated (GtkTreeView *view,
 {
   EmpathyIndividualViewPriv *priv = GET_PRIV (view);
   FolksIndividual *individual;
-  EmpathyContact *contact = NULL;
+  EmpathyContact *contact;
   GtkTreeModel *model;
   GtkTreeIter iter;
 
@@ -917,7 +974,10 @@ individual_view_row_activated (GtkTreeView *view,
   if (individual == NULL)
     return;
 
-  contact = empathy_contact_dup_from_folks_individual (individual);
+  /* Determine which Persona to chat to, by choosing the most available one. */
+  contact = empathy_contact_dup_best_for_action (individual,
+      EMPATHY_ACTION_CHAT);
+
   if (contact != NULL)
     {
       DEBUG ("Starting a chat");
@@ -935,6 +995,7 @@ individual_view_call_activated_cb (EmpathyCellRendererActivatable *cell,
     const gchar *path_string,
     EmpathyIndividualView *view)
 {
+  EmpathyIndividualViewPriv *priv = GET_PRIV (view);
   GtkWidget *menu;
   GtkTreeModel *model;
   GtkTreeIter iter;
@@ -943,6 +1004,9 @@ individual_view_call_activated_cb (EmpathyCellRendererActivatable *cell,
   GtkMenuShell *shell;
   GtkWidget *item;
 
+  if (!(priv->view_features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_CALL))
+    return;
+
   model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
   if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
     return;
@@ -967,7 +1031,6 @@ individual_view_call_activated_cb (EmpathyCellRendererActivatable *cell,
   gtk_menu_shell_append (shell, item);
   gtk_widget_show (item);
 
-  g_signal_connect (menu, "deactivate", G_CALLBACK (gtk_menu_detach), NULL);
   gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (view), NULL);
   gtk_widget_show (menu);
   gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
@@ -1215,10 +1278,7 @@ individual_view_start_search_cb (EmpathyIndividualView *view,
   if (priv->search_widget == NULL)
     return FALSE;
 
-  if (gtk_widget_get_visible (GTK_WIDGET (priv->search_widget)))
-    gtk_widget_grab_focus (GTK_WIDGET (priv->search_widget));
-  else
-    gtk_widget_show (GTK_WIDGET (priv->search_widget));
+  empathy_individual_view_start_search (view);
 
   return TRUE;
 }
@@ -1306,7 +1366,7 @@ individual_view_search_key_navigation_cb (GtkWidget *search,
   GdkEventKey *eventkey = ((GdkEventKey *) event);
   gboolean ret = FALSE;
 
-  if (eventkey->keyval == GDK_Up || eventkey->keyval == GDK_Down)
+  if (eventkey->keyval == GDK_KEY_Up || eventkey->keyval == GDK_KEY_Down)
     {
       GdkEvent *new_event;
 
@@ -1473,8 +1533,12 @@ individual_view_expand_idle_cb (EmpathyIndividualView *self)
   g_signal_handlers_unblock_by_func (self,
       individual_view_row_expand_or_collapse_cb, GINT_TO_POINTER (TRUE));
 
-  g_object_unref (self);
+  /* Empty the table of groups to expand/contract, since it may contain groups
+   * which no longer exist in the tree view. This can happen after going
+   * offline, for example. */
+  g_hash_table_remove_all (priv->expand_groups);
   priv->expand_groups_idle_handler = 0;
+  g_object_unref (self);
 
   return FALSE;
 }
@@ -1512,7 +1576,7 @@ individual_view_row_has_child_toggled_cb (GtkTreeModel *model,
    * a hash table, and expand or contract them as appropriate all at once in
    * an idle handler which iterates over all the group rows. */
   if (g_hash_table_lookup_extended (priv->expand_groups, name, NULL,
-      &will_expand) == FALSE &&
+      &will_expand) == FALSE ||
       GPOINTER_TO_INT (will_expand) != should_expand)
     {
       g_hash_table_insert (priv->expand_groups, g_strdup (name),
@@ -1579,7 +1643,9 @@ individual_view_store_row_deleted_cb (GtkTreeModel *model,
 
 static gboolean
 individual_view_is_visible_individual (EmpathyIndividualView *self,
-    FolksIndividual *individual)
+    FolksIndividual *individual,
+    gboolean is_online,
+    gboolean is_searching)
 {
   EmpathyIndividualViewPriv *priv = GET_PRIV (self);
   EmpathyLiveSearch *live = EMPATHY_LIVE_SEARCH (priv->search_widget);
@@ -1588,11 +1654,17 @@ individual_view_is_visible_individual (EmpathyIndividualView *self,
 
   /* We're only giving the visibility wrt filtering here, not things like
    * presence. */
-  if (live == NULL || gtk_widget_get_visible (GTK_WIDGET (live)) == FALSE)
-    return TRUE;
+  if (priv->show_untrusted == FALSE &&
+      folks_individual_get_trust_level (individual) == FOLKS_TRUST_LEVEL_NONE)
+    {
+      return FALSE;
+    }
+
+  if (is_searching == FALSE)
+    return (priv->show_offline || is_online);
 
   /* check alias name */
-  str = folks_individual_get_alias (individual);
+  str = folks_aliasable_get_alias (FOLKS_ALIASABLE (individual));
 
   if (empathy_live_search_match (live, str))
     return TRUE;
@@ -1651,10 +1723,8 @@ individual_view_filter_visible_func (GtkTreeModel *model,
 
   if (individual != NULL)
     {
-      if (is_searching == TRUE)
-        visible = individual_view_is_visible_individual (self, individual);
-      else
-        visible = (priv->show_offline || is_online);
+      visible = individual_view_is_visible_individual (self, individual,
+          is_online, is_searching);
 
       g_object_unref (individual);
 
@@ -1687,12 +1757,12 @@ individual_view_filter_visible_func (GtkTreeModel *model,
       if (individual == NULL)
         continue;
 
-      visible = individual_view_is_visible_individual (self, individual);
+      visible = individual_view_is_visible_individual (self, individual,
+          is_online, is_searching);
       g_object_unref (individual);
 
       /* show group if it has at least one visible contact in it */
-      if ((is_searching && visible) ||
-          (!is_searching && (priv->show_offline || is_online)))
+      if (visible == TRUE)
         return TRUE;
     }
 
@@ -1761,6 +1831,8 @@ individual_view_constructed (GObject *object)
       "is_group", EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP);
   gtk_tree_view_column_add_attribute (col, cell,
       "compact", EMPATHY_INDIVIDUAL_STORE_COL_COMPACT);
+  gtk_tree_view_column_add_attribute (col, cell,
+      "client-types", EMPATHY_INDIVIDUAL_STORE_COL_CLIENT_TYPES);
 
   /* Audio Call Icon */
   cell = empathy_cell_renderer_activatable_new ();
@@ -1886,6 +1958,8 @@ individual_view_finalize (GObject *object)
 {
   EmpathyIndividualViewPriv *priv = GET_PRIV (object);
 
+  if (priv->expand_groups_idle_handler != 0)
+    g_source_remove (priv->expand_groups_idle_handler);
   g_hash_table_destroy (priv->expand_groups);
 
   G_OBJECT_CLASS (empathy_individual_view_parent_class)->finalize (object);
@@ -1915,6 +1989,9 @@ individual_view_get_property (GObject *object,
     case PROP_SHOW_OFFLINE:
       g_value_set_boolean (value, priv->show_offline);
       break;
+    case PROP_SHOW_UNTRUSTED:
+      g_value_set_boolean (value, priv->show_untrusted);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
       break;
@@ -1945,6 +2022,10 @@ individual_view_set_property (GObject *object,
       empathy_individual_view_set_show_offline (view,
           g_value_get_boolean (value));
       break;
+    case PROP_SHOW_UNTRUSTED:
+      empathy_individual_view_set_show_untrusted (view,
+          g_value_get_boolean (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
       break;
@@ -2024,6 +2105,13 @@ empathy_individual_view_class_init (EmpathyIndividualViewClass *klass)
           "Show Offline",
           "Whether contact list should display "
           "offline contacts", FALSE, G_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+      PROP_SHOW_UNTRUSTED,
+      g_param_spec_boolean ("show-untrusted",
+          "Show Untrusted Individuals",
+          "Whether the view should display untrusted individuals; "
+          "those who could not be who they say they are.",
+          TRUE, G_PARAM_READWRITE));
 
   g_type_class_add_private (object_class, sizeof (EmpathyIndividualViewPriv));
 }
@@ -2035,6 +2123,9 @@ empathy_individual_view_init (EmpathyIndividualView *view)
       EMPATHY_TYPE_INDIVIDUAL_VIEW, EmpathyIndividualViewPriv);
 
   view->priv = priv;
+
+  priv->show_untrusted = TRUE;
+
   /* Get saved group states. */
   empathy_contact_groups_get_all ();
 
@@ -2095,31 +2186,8 @@ empathy_individual_view_dup_selected (EmpathyIndividualView *view)
   return individual;
 }
 
-EmpathyIndividualManagerFlags
-empathy_individual_view_get_flags (EmpathyIndividualView *view)
-{
-  EmpathyIndividualViewPriv *priv;
-  GtkTreeSelection *selection;
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-  EmpathyIndividualFeatureFlags flags;
-
-  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (view), 0);
-
-  priv = GET_PRIV (view);
-
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
-  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
-    return 0;
-
-  gtk_tree_model_get (model, &iter,
-      EMPATHY_INDIVIDUAL_STORE_COL_FLAGS, &flags, -1);
-
-  return flags;
-}
-
-gchar *
-empathy_individual_view_get_selected_group (EmpathyIndividualView *view,
+static gchar *
+empathy_individual_view_dup_selected_group (EmpathyIndividualView *view,
     gboolean *is_fake_group)
 {
   EmpathyIndividualViewPriv *priv;
@@ -2185,7 +2253,7 @@ individual_view_group_remove_activate_cb (GtkMenuItem *menuitem,
 {
   gchar *group;
 
-  group = empathy_individual_view_get_selected_group (view, NULL);
+  group = empathy_individual_view_dup_selected_group (view, NULL);
   if (group != NULL)
     {
       gchar *text;
@@ -2226,10 +2294,11 @@ empathy_individual_view_get_group_menu (EmpathyIndividualView *view)
               EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_REMOVE)))
     return NULL;
 
-  group = empathy_individual_view_get_selected_group (view, &is_fake_group);
+  group = empathy_individual_view_dup_selected_group (view, &is_fake_group);
   if (!group || is_fake_group)
     {
       /* We can't alter fake groups */
+      g_free (group);
       return NULL;
     }
 
@@ -2281,7 +2350,7 @@ individual_view_remove_activate_cb (GtkMenuItem *menuitem,
       text =
           g_strdup_printf (_
           ("Do you really want to remove the contact '%s'?"),
-          folks_individual_get_alias (individual));
+          folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
       if (individual_view_remove_dialog_show (parent, _("Removing contact"),
               text))
         {
@@ -2297,6 +2366,19 @@ individual_view_remove_activate_cb (GtkMenuItem *menuitem,
     }
 }
 
+static void
+individual_menu_link_contacts_activated_cb (EmpathyIndividualMenu *menu,
+    EmpathyLinkingDialog *linking_dialog,
+    EmpathyIndividualView *self)
+{
+  EmpathyIndividualViewPriv *priv = GET_PRIV (self);
+  EmpathyIndividualLinker *linker;
+
+  linker = empathy_linking_dialog_get_individual_linker (linking_dialog);
+  empathy_individual_linker_set_search_text (linker,
+      empathy_live_search_get_text (EMPATHY_LIVE_SEARCH (priv->search_widget)));
+}
+
 GtkWidget *
 empathy_individual_view_get_individual_menu (EmpathyIndividualView *view)
 {
@@ -2305,7 +2387,8 @@ empathy_individual_view_get_individual_menu (EmpathyIndividualView *view)
   GtkWidget *menu = NULL;
   GtkWidget *item;
   GtkWidget *image;
-  EmpathyIndividualManagerFlags flags;
+  gboolean can_remove = FALSE;
+  GList *l;
 
   g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (view), NULL);
 
@@ -2313,16 +2396,31 @@ empathy_individual_view_get_individual_menu (EmpathyIndividualView *view)
   if (individual == NULL)
     return NULL;
 
-  flags = empathy_individual_view_get_flags (view);
+  /* If any of the Individual's personas can be removed, add an option to
+   * remove. This will act as a best-effort option. If any Personas cannot be
+   * removed from the server, then this option will just be inactive upon
+   * subsequent menu openings */
+  for (l = folks_individual_get_personas (individual); l != NULL; l = l->next)
+    {
+      FolksPersona *persona = FOLKS_PERSONA (l->data);
+      FolksPersonaStore *store = folks_persona_get_store (persona);
+      FolksMaybeBool maybe_can_remove =
+          folks_persona_store_get_can_remove_personas (store);
+
+      if (maybe_can_remove == FOLKS_MAYBE_BOOL_TRUE)
+        {
+          can_remove = TRUE;
+          break;
+        }
+    }
 
   menu = empathy_individual_menu_new (individual, priv->individual_features);
 
   /* Remove contact */
-  if (priv->view_features &
-      EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_REMOVE &&
-      flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_REMOVE)
+  if ((priv->view_features &
+      EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_REMOVE) &&
+      can_remove)
     {
-
       /* create the menu if required, or just add a separator */
       if (menu == NULL)
         menu = gtk_menu_new ();
@@ -2344,6 +2442,12 @@ empathy_individual_view_get_individual_menu (EmpathyIndividualView *view)
           G_CALLBACK (individual_view_remove_activate_cb), view);
     }
 
+  /* Connect to EmpathyIndividualMenu::link-contacts-activated so that we can
+   * set the live search text on the new linking dialogue to be the same as
+   * our own. */
+  g_signal_connect (menu, "link-contacts-activated",
+      (GCallback) individual_menu_link_contacts_activated_cb, view);
+
   g_object_unref (individual);
 
   return menu;
@@ -2437,6 +2541,30 @@ empathy_individual_view_set_show_offline (EmpathyIndividualView *self,
   gtk_tree_model_filter_refilter (priv->filter);
 }
 
+gboolean
+empathy_individual_view_get_show_untrusted (EmpathyIndividualView *self)
+{
+  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (self), FALSE);
+
+  return GET_PRIV (self)->show_untrusted;
+}
+
+void
+empathy_individual_view_set_show_untrusted (EmpathyIndividualView *self,
+    gboolean show_untrusted)
+{
+  EmpathyIndividualViewPriv *priv;
+
+  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (self));
+
+  priv = GET_PRIV (self);
+
+  priv->show_untrusted = show_untrusted;
+
+  g_object_notify (G_OBJECT (self), "show-untrusted");
+  gtk_tree_model_filter_refilter (priv->filter);
+}
+
 EmpathyIndividualStore *
 empathy_individual_view_get_store (EmpathyIndividualView *self)
 {
@@ -2499,3 +2627,17 @@ empathy_individual_view_set_store (EmpathyIndividualView *self,
           G_CALLBACK (individual_view_store_row_deleted_cb), self, 0);
     }
 }
+
+void
+empathy_individual_view_start_search (EmpathyIndividualView *self)
+{
+  EmpathyIndividualViewPriv *priv = GET_PRIV (self);
+
+  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (self));
+  g_return_if_fail (priv->search_widget != NULL);
+
+  if (gtk_widget_get_visible (GTK_WIDGET (priv->search_widget)))
+    gtk_widget_grab_focus (GTK_WIDGET (priv->search_widget));
+  else
+    gtk_widget_show (GTK_WIDGET (priv->search_widget));
+}