+tooltip_destroy_cb (GtkWidget *widget,
+ EmpathyRosterWindow *self)
+{
+ g_clear_object (&self->priv->tooltip_widget);
+}
+
+static gboolean
+individual_tooltip_cb (EmpathyRosterView *view,
+ FolksIndividual *individual,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ EmpathyRosterWindow *self)
+{
+ if (self->priv->tooltip_widget == NULL)
+ {
+ self->priv->tooltip_widget = empathy_individual_widget_new (individual,
+ EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP |
+ EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION |
+ EMPATHY_INDIVIDUAL_WIDGET_SHOW_CLIENT_TYPES);
+
+ gtk_container_set_border_width (
+ GTK_CONTAINER (self->priv->tooltip_widget), 8);
+
+ g_object_ref (self->priv->tooltip_widget);
+
+ tp_g_signal_connect_object (self->priv->tooltip_widget, "destroy",
+ G_CALLBACK (tooltip_destroy_cb), self, 0);
+
+ gtk_widget_show (self->priv->tooltip_widget);
+ }
+ else
+ {
+ empathy_individual_widget_set_individual (
+ EMPATHY_INDIVIDUAL_WIDGET (self->priv->tooltip_widget), individual);
+ }
+
+ gtk_tooltip_set_custom (tooltip, self->priv->tooltip_widget);
+
+ return TRUE;
+}
+
+typedef enum
+{
+ DND_DRAG_TYPE_INVALID = -1,
+ DND_DRAG_TYPE_URI_LIST,
+} DndDragType;
+
+#define DRAG_TYPE(T,I) \
+ { (gchar *) T, 0, I }
+
+static const GtkTargetEntry drag_types_dest[] = {
+ DRAG_TYPE ("text/path-list", DND_DRAG_TYPE_URI_LIST),
+ DRAG_TYPE ("text/uri-list", DND_DRAG_TYPE_URI_LIST),
+};
+
+static GdkAtom drag_atoms_dest[G_N_ELEMENTS (drag_types_dest)];
+
+static DndDragType
+get_drag_type (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ GdkAtom target;
+ guint i;
+
+ target = gtk_drag_dest_find_target (widget, context, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (drag_atoms_dest); i++)
+ {
+ if (target == drag_atoms_dest[i])
+ return drag_types_dest[i].info;
+ }
+
+ return DND_DRAG_TYPE_INVALID;
+}
+
+static gboolean
+individual_supports_ft (FolksIndividual *individual)
+{
+ EmpathyContact *contact;
+ EmpathyCapabilities caps;
+ gboolean result;
+
+ contact = empathy_contact_dup_from_folks_individual (individual);
+ if (contact == NULL)
+ return FALSE;
+
+ caps = empathy_contact_get_capabilities (contact);
+ result = (caps & EMPATHY_CAPABILITIES_FT);
+
+ g_object_unref (contact);
+ return result;
+}
+
+static gboolean
+view_drag_motion_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_,
+ EmpathyRosterWindow *self)
+{
+ DndDragType type;
+
+ type = get_drag_type (widget, context);
+
+ if (type == DND_DRAG_TYPE_URI_LIST)
+ {
+ /* Check if contact supports FT */
+ FolksIndividual *individual;
+ GtkListBoxRow *row;
+
+ individual = empathy_roster_view_get_individual_at_y (self->priv->view,
+ y, &row);
+ if (individual == NULL)
+ goto no_hl;
+
+ if (!individual_supports_ft (individual))
+ goto no_hl;
+
+ gtk_list_box_drag_highlight_row (GTK_LIST_BOX (widget), row);
+ return FALSE;
+ }
+
+no_hl:
+ gtk_list_box_drag_unhighlight_row (GTK_LIST_BOX (widget));
+ return FALSE;
+}
+
+static gboolean
+view_drag_drop_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_,