+static void
+view_empty_cb (EmpathyRosterView *view,
+ GParamSpec *spec,
+ EmpathyRosterWindow *self)
+{
+ set_notebook_page (self);
+
+ if (!empathy_roster_view_is_empty (view))
+ {
+ gtk_widget_grab_focus (GTK_WIDGET (self->priv->view));
+
+ /* The store is being filled, it will be done after an idle cb.
+ * So we can then get events. If we do that too soon, event's
+ * contact is not yet in the store and it won't get marked as
+ * having events. */
+ g_idle_add (roster_window_load_events_idle_cb, self);
+ }
+}
+
+static void
+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_,
+ EmpathyRosterWindow *self)
+{
+ DndDragType type;
+ FolksIndividual *individual;
+
+ type = get_drag_type (widget, context);
+ if (type == DND_DRAG_TYPE_INVALID)
+ return FALSE;
+
+ individual = empathy_roster_view_get_individual_at_y (self->priv->view, y,
+ NULL);
+ if (individual == NULL)
+ return FALSE;
+
+ if (!individual_supports_ft (individual))
+ return FALSE;
+
+ gtk_drag_get_data (widget, context,
+ gtk_drag_dest_find_target (widget, context, NULL), time_);
+
+ return TRUE;
+}
+
+static void
+view_drag_data_received_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time_,
+ EmpathyRosterWindow *self)
+{
+ gboolean success = FALSE;
+
+ if (selection == NULL)
+ goto out;
+
+ if (info == DND_DRAG_TYPE_URI_LIST)
+ {
+ const gchar *path;
+ FolksIndividual *individual;
+ EmpathyContact *contact;
+
+ individual = empathy_roster_view_get_individual_at_y (self->priv->view,
+ y, NULL);
+ g_return_if_fail (individual != NULL);
+
+ path = (const gchar *) gtk_selection_data_get_data (selection);
+
+ contact = empathy_contact_dup_from_folks_individual (individual);
+ empathy_send_file_from_uri_list (contact, path);
+
+ g_object_unref (contact);
+
+ success = TRUE;
+ }
+
+out:
+ gtk_drag_finish (context, success, FALSE, time_);
+}
+
+static void
+roster_window_most_available_presence_changed_cb (TpAccountManager *manager,
+ TpConnectionPresenceType presence,
+ const gchar *status,
+ const gchar *message,
+ EmpathyRosterWindow *self)
+{
+ set_notebook_page (self);
+}
+
+static void
+show_offline_changed_cb (GSettings *settings,
+ const gchar *key,
+ EmpathyRosterWindow *self)
+{
+ set_notebook_page (self);
+}
+