diff options
-rw-r--r-- | libempathy-gtk/empathy-avatar-chooser.c | 417 | ||||
-rw-r--r-- | libempathy-gtk/empathy-avatar-chooser.h | 18 |
2 files changed, 235 insertions, 200 deletions
diff --git a/libempathy-gtk/empathy-avatar-chooser.c b/libempathy-gtk/empathy-avatar-chooser.c index 0a04fe0c0..f75b4fdb9 100644 --- a/libempathy-gtk/empathy-avatar-chooser.c +++ b/libempathy-gtk/empathy-avatar-chooser.c @@ -78,29 +78,22 @@ struct _EmpathyAvatarChooserPrivate { - TpConnection *connection; - GtkFileChooser *chooser_dialog; + TpAccount *account; - gulong ready_handler_id; + GArray *avatar; + gchar *mime_type; + gboolean changed; - EmpathyAvatar *avatar; + GtkFileChooser *chooser_dialog; GSettings *gsettings_ui; }; enum { - CHANGED, - LAST_SIGNAL -}; - -enum -{ PROP_0, - PROP_CONNECTION + PROP_ACCOUNT }; -static guint signals [LAST_SIGNAL]; - G_DEFINE_TYPE (EmpathyAvatarChooser, empathy_avatar_chooser, GTK_TYPE_BUTTON); /* @@ -118,6 +111,88 @@ static const GtkTargetEntry drop_types[] = { URI_LIST_TYPE, 0, DND_TARGET_TYPE_URI_LIST }, }; +static void avatar_chooser_set_image (EmpathyAvatarChooser *self, + GArray *avatar, + gchar *mime_type, + GdkPixbuf *pixbuf, + gboolean maybe_convert); +static void avatar_chooser_clear_image (EmpathyAvatarChooser *self); + +static void +get_avatar_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpWeakRef *weak = user_data; + EmpathyAvatarChooser *self = tp_weak_ref_dup_object (weak); + const GArray *avatar; + GdkPixbuf *pixbuf; + gchar *mime_type; + GError *error = NULL; + + if (self == NULL) + goto out; + + avatar = tp_account_get_avatar_finish (self->priv->account, result, &error); + if (avatar == NULL) + { + DEBUG ("Error getting account's avatar: %s", error->message); + g_clear_error (&error); + goto out; + } + + if (avatar->len == 0) + { + avatar_chooser_clear_image (self); + goto out; + } + + pixbuf = empathy_pixbuf_from_data_and_mime ((gchar *) avatar->data, + avatar->len, &mime_type); + if (pixbuf == NULL) + { + DEBUG ("couldn't make a pixbuf from avatar; giving up"); + goto out; + } + + avatar_chooser_set_image (self, (GArray *) avatar, mime_type, pixbuf, FALSE); + g_free (mime_type); + + self->priv->changed = FALSE; + g_object_unref (self); + +out: + tp_weak_ref_destroy (weak); +} + +static void +avatar_changed_cb (TpAccount *account, + gpointer user_data, + GObject *weak_object) +{ + EmpathyAvatarChooser *self = (EmpathyAvatarChooser *) weak_object; + + tp_account_get_avatar_async (self->priv->account, + get_avatar_cb, tp_weak_ref_new (self, NULL, NULL)); +} + +static void +avatar_chooser_constructed (GObject *object) +{ + EmpathyAvatarChooser *self = (EmpathyAvatarChooser *) object; + + G_OBJECT_CLASS (empathy_avatar_chooser_parent_class)->constructed (object); + + tp_account_get_avatar_async (self->priv->account, + get_avatar_cb, tp_weak_ref_new (self, NULL, NULL)); + + /* FIXME: no signal on TpAccount, yet. + * See https://bugs.freedesktop.org/show_bug.cgi?id=52938 */ + tp_cli_account_interface_avatar_connect_to_avatar_changed ( + self->priv->account, avatar_changed_cb, NULL, NULL, (GObject *) self, + NULL); +} + static void avatar_chooser_get_property (GObject *object, guint param_id, @@ -128,8 +203,8 @@ avatar_chooser_get_property (GObject *object, switch (param_id) { - case PROP_CONNECTION: - g_value_set_object (value, self->priv->connection); + case PROP_ACCOUNT: + g_value_set_object (value, self->priv->account); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -138,16 +213,6 @@ avatar_chooser_get_property (GObject *object, } static void -avatar_chooser_set_connection (EmpathyAvatarChooser *self, - TpConnection *connection) -{ - tp_clear_object (&self->priv->connection); - - if (connection != NULL) - self->priv->connection = g_object_ref (connection); -} - -static void avatar_chooser_set_property (GObject *object, guint param_id, const GValue *value, @@ -157,8 +222,9 @@ avatar_chooser_set_property (GObject *object, switch (param_id) { - case PROP_CONNECTION: - avatar_chooser_set_connection (self, g_value_get_object (value)); + case PROP_ACCOUNT: + g_assert (self->priv->account == NULL); /* construct-only */ + self->priv->account = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -171,8 +237,9 @@ avatar_chooser_dispose (GObject *object) { EmpathyAvatarChooser *self = (EmpathyAvatarChooser *) object; - tp_clear_object (&self->priv->connection); - tp_clear_pointer (&self->priv->avatar, empathy_avatar_unref); + tp_clear_object (&self->priv->account); + tp_clear_pointer (&self->priv->avatar, g_array_unref); + tp_clear_pointer (&self->priv->mime_type, g_free); tp_clear_object (&self->priv->gsettings_ui); G_OBJECT_CLASS (empathy_avatar_chooser_parent_class)->dispose (object); @@ -184,41 +251,27 @@ empathy_avatar_chooser_class_init (EmpathyAvatarChooserClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; + object_class->constructed = avatar_chooser_constructed; object_class->dispose = avatar_chooser_dispose; object_class->get_property = avatar_chooser_get_property; object_class->set_property = avatar_chooser_set_property; /** - * EmpathyAvatarChooser::changed: - * @self: an #EmpathyAvatarChooser - * - * Emitted when the chosen avatar has changed. - * - */ - signals[CHANGED] = - g_signal_new ("changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_generic, - G_TYPE_NONE, 0); - - /** - * EmpathyAvatarChooser:connection: + * EmpathyAvatarChooser:account: * - * The #TpConnection whose avatar should be shown and modified by + * The #TpAccount whose avatar should be shown and modified by * the #EmpathyAvatarChooser instance. */ - param_spec = g_param_spec_object ("connection", - "TpConnection", - "TpConnection whose avatar should be " + param_spec = g_param_spec_object ("account", + "TpAccount", + "TpAccount whose avatar should be " "shown and modified by this widget", - TP_TYPE_CONNECTION, + TP_TYPE_ACCOUNT, G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, - PROP_CONNECTION, + PROP_ACCOUNT, param_spec); g_type_class_add_private (object_class, sizeof (EmpathyAvatarChooserPrivate)); @@ -295,12 +348,13 @@ avatar_chooser_clear_image (EmpathyAvatarChooser *self) { GtkWidget *image; - tp_clear_pointer (&self->priv->avatar, empathy_avatar_unref); + tp_clear_pointer (&self->priv->avatar, g_array_unref); + tp_clear_pointer (&self->priv->mime_type, g_free); + self->priv->changed = TRUE; image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_AVATAR_DEFAULT, GTK_ICON_SIZE_DIALOG); gtk_button_set_image (GTK_BUTTON (self), image); - g_signal_emit (self, signals[CHANGED], 0); } static gboolean @@ -426,10 +480,24 @@ avatar_chooser_error_show (EmpathyAvatarChooser *self, } -static EmpathyAvatar * +static TpAvatarRequirements * +get_requirements (EmpathyAvatarChooser *self) +{ + TpConnection *connection; + + /* FIXME: Should get on TpProtocol if account is offline */ + connection = tp_account_get_connection (self->priv->account); + return tp_connection_get_avatar_requirements (connection); +} + + +static gboolean avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *self, GdkPixbuf *pixbuf, - EmpathyAvatar *avatar) + GArray *avatar, + gchar *mime_type, + GArray **ret_avatar, + gchar **ret_mime_type) { TpAvatarRequirements *req; gboolean needs_conversion = FALSE; @@ -442,11 +510,14 @@ avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *self, gsize best_image_size = 0; guint count = 0; - req = tp_connection_get_avatar_requirements (self->priv->connection); + g_assert (ret_avatar != NULL); + g_assert (ret_mime_type != NULL); + + req = get_requirements (self); if (req == NULL) { DEBUG ("Avatar requirements not ready"); - return NULL; + return FALSE; } /* Smaller is the factor, smaller will be the image. @@ -456,12 +527,12 @@ avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *self, factor = 1; /* Check if we need to convert to another image format */ - if (avatar_chooser_need_mime_type_conversion (avatar->format, + if (avatar_chooser_need_mime_type_conversion (mime_type, req->supported_mime_types, &new_format_name, &new_mime_type)) { DEBUG ("Format conversion needed, we'll use mime type '%s' " "and format name '%s'. Current mime type is '%s'", - new_mime_type, new_format_name, avatar->format); + new_mime_type, new_format_name, mime_type); needs_conversion = TRUE; } @@ -471,7 +542,7 @@ avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *self, avatar_chooser_error_show (self, _("Couldn't convert image"), _("None of the accepted image formats are " "supported on your system")); - return NULL; + return FALSE; } /* If width or height are too big, it needs converting. */ @@ -497,7 +568,7 @@ avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *self, if (req->maximum_bytes > 0 && avatar->len > req->maximum_bytes && !needs_conversion) { - DEBUG ("Image data (%"G_GSIZE_FORMAT" bytes) is too big " + DEBUG ("Image data (%u bytes) is too big " "(max is %u bytes), conversion needed.", avatar->len, req->maximum_bytes); @@ -508,9 +579,9 @@ avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *self, /* If no conversion is needed, return the avatar */ if (!needs_conversion) { - g_free (new_format_name); - g_free (new_mime_type); - return empathy_avatar_ref (avatar); + *ret_avatar = g_array_ref (avatar); + *ret_mime_type = g_strdup (mime_type); + return TRUE; } do @@ -556,7 +627,7 @@ avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *self, _("Couldn't convert image"), error ? error->message : NULL); g_clear_error (&error); - return NULL; + return FALSE; } DEBUG ("Produced an image data of %"G_GSIZE_FORMAT" bytes.", @@ -604,20 +675,24 @@ avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *self, g_free (new_format_name); - avatar = empathy_avatar_new ((guchar *) best_image_data, - best_image_size, new_mime_type, NULL); - + /* FIXME: there is no way to create a GArray with zero copy? */ + *ret_avatar = g_array_sized_new (FALSE, FALSE, sizeof (gchar), + best_image_size); + g_array_append_vals (*ret_avatar, best_image_data, best_image_size); g_free (best_image_data); - g_free (new_mime_type); - return avatar; + *ret_mime_type = new_mime_type; + + return TRUE; } +/* Take ownership of @pixbuf */ static void avatar_chooser_set_image (EmpathyAvatarChooser *self, - EmpathyAvatar *avatar, + GArray *avatar, + gchar *mime_type, GdkPixbuf *pixbuf, - gboolean set_locally) + gboolean maybe_convert) { GdkPixbuf *pixbuf_view; GtkWidget *image; @@ -625,30 +700,38 @@ avatar_chooser_set_image (EmpathyAvatarChooser *self, g_assert (avatar != NULL); g_assert (pixbuf != NULL); - if (set_locally) + if (maybe_convert) { - EmpathyAvatar *conv; - - conv = avatar_chooser_maybe_convert_and_scale (self, - pixbuf, avatar); - empathy_avatar_unref (avatar); + GArray *conv_avatar = NULL; + gchar *conv_mime_type = NULL; - if (conv == NULL) - /* An error occured; don't change the avatar. */ + if (!avatar_chooser_maybe_convert_and_scale (self, + pixbuf, avatar, mime_type, &conv_avatar, &conv_mime_type)) return; - avatar = conv; + /* Transfer ownership */ + tp_clear_pointer (&self->priv->avatar, g_array_unref); + self->priv->avatar = conv_avatar; + + g_free (self->priv->mime_type); + self->priv->mime_type = conv_mime_type; + } + else + { + tp_clear_pointer (&self->priv->avatar, g_array_unref); + self->priv->avatar = g_array_ref (avatar); + + g_free (self->priv->mime_type); + self->priv->mime_type = g_strdup (mime_type); } - tp_clear_pointer (&self->priv->avatar, empathy_avatar_unref); - self->priv->avatar = avatar; + self->priv->changed = TRUE; pixbuf_view = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_VIEW); image = gtk_image_new_from_pixbuf (pixbuf_view); gtk_button_set_image (GTK_BUTTON (self), image); - g_signal_emit (self, signals[CHANGED], 0); g_object_unref (pixbuf_view); g_object_unref (pixbuf); @@ -658,11 +741,10 @@ avatar_chooser_set_image (EmpathyAvatarChooser *self, static void avatar_chooser_set_image_from_data (EmpathyAvatarChooser *self, gchar *data, - gsize size, - gboolean set_locally) + gsize size) { GdkPixbuf *pixbuf; - EmpathyAvatar *avatar = NULL; + GArray *avatar; gchar *mime_type = NULL; if (data == NULL) @@ -675,15 +757,17 @@ avatar_chooser_set_image_from_data (EmpathyAvatarChooser *self, if (pixbuf == NULL) { g_free (data); - data = NULL; return; } - avatar = empathy_avatar_new ((guchar *) data, size, mime_type, NULL); + /* FIXME: there is no way to create a GArray with zero copy? */ + avatar = g_array_sized_new (FALSE, FALSE, sizeof (gchar), size); + g_array_append_vals (avatar, data, size); - avatar_chooser_set_image (self, avatar, pixbuf, set_locally); + avatar_chooser_set_image (self, avatar, mime_type, pixbuf, TRUE); g_free (mime_type); + g_array_unref (avatar); g_free (data); } @@ -733,7 +817,7 @@ avatar_chooser_drag_data_received_cb (GtkWidget *widget, if (handled) { /* pass data to the avatar_chooser_set_image_from_data */ - avatar_chooser_set_image_from_data (self, data, bytes_read, TRUE); + avatar_chooser_set_image_from_data (self, data, bytes_read); } g_object_unref (file); @@ -800,7 +884,7 @@ avatar_chooser_set_image_from_file (EmpathyAvatarChooser *self, } /* pass image_data to the avatar_chooser_set_image_from_data */ - avatar_chooser_set_image_from_data (self, image_data, image_size, TRUE); + avatar_chooser_set_image_from_data (self, image_data, image_size); } #ifdef HAVE_CHEESE @@ -810,7 +894,7 @@ avatar_chooser_set_avatar_from_pixbuf (EmpathyAvatarChooser *self, { gsize size; gchar *buf; - EmpathyAvatar *avatar = NULL; + GArray *avatar; GError *error = NULL; if (!gdk_pixbuf_save_to_buffer (pb, &buf, &size, "png", &error, NULL)) @@ -822,10 +906,14 @@ avatar_chooser_set_avatar_from_pixbuf (EmpathyAvatarChooser *self, return; } - avatar = empathy_avatar_new ((guchar *) buf, size, "image/png", NULL); - avatar_chooser_set_image (self, avatar, pb, TRUE); + /* FIXME: there is no way to create a GArray with zero copy? */ + avatar = g_array_sized_new (FALSE, FALSE, sizeof (gchar), size); + g_array_append_vals (avatar, buf, size); + + avatar_chooser_set_image (self, avatar, "image/png", pb, TRUE); g_free (buf); + g_array_unref (avatar); } static gboolean @@ -1062,125 +1150,76 @@ empathy_avatar_chooser_init (EmpathyAvatarChooser *self) G_CALLBACK (avatar_chooser_clicked_cb), self); - empathy_avatar_chooser_set (self, NULL); -} - -static void -avatar_chooser_set_image_from_avatar (EmpathyAvatarChooser *self, - EmpathyAvatar *avatar, - gboolean set_locally) -{ - GdkPixbuf *pixbuf; - gchar *mime_type = NULL; - - g_assert (avatar != NULL); - - pixbuf = empathy_pixbuf_from_data_and_mime ((gchar *) avatar->data, - avatar->len, &mime_type); - - if (pixbuf == NULL) - { - DEBUG ("couldn't make a pixbuf from avatar; giving up"); - return; - } - - if (avatar->format == NULL) - { - avatar->format = mime_type; - } - else - { - if (strcmp (mime_type, avatar->format)) - DEBUG ("avatar->format is %s; gdkpixbuf yields %s!", - avatar->format, mime_type); - - g_free (mime_type); - } - - empathy_avatar_ref (avatar); - - avatar_chooser_set_image (self, avatar, pixbuf, set_locally); + avatar_chooser_clear_image (self); } /** * empathy_avatar_chooser_new: + * @account: a #TpAccount * * Creates a new #EmpathyAvatarChooser. * * Return value: a new #EmpathyAvatarChooser */ GtkWidget * -empathy_avatar_chooser_new (void) +empathy_avatar_chooser_new (TpAccount *account) { - return g_object_new (EMPATHY_TYPE_AVATAR_CHOOSER, NULL); + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return g_object_new (EMPATHY_TYPE_AVATAR_CHOOSER, + "account", account, + NULL); } -/** - * empathy_avatar_chooser_set: - * @self: an #EmpathyAvatarChooser - * @avatar: a new #EmpathyAvatar - * - * Sets the @self to display the avatar indicated by @avatar. - */ -void -empathy_avatar_chooser_set (EmpathyAvatarChooser *self, - EmpathyAvatar *avatar) +static void +set_avatar_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) { - g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (self)); + GSimpleAsyncResult *my_result = user_data; + GError *error = NULL; - if (avatar != NULL) - avatar_chooser_set_image_from_avatar (self, avatar, FALSE); - else - avatar_chooser_clear_image (self); + if (!tp_account_set_avatar_finish (TP_ACCOUNT (source), result, &error)) + g_simple_async_result_take_error (my_result, error); + + g_simple_async_result_complete (my_result); + g_object_unref (my_result); } -/** - * empathy_avatar_chooser_get_image_data: - * @self: an #EmpathyAvatarChooser - * @data: avatar bytes - * @data_size: size of @data - * @mime_type: avatar mime-type - * - * Gets image data about the currently selected avatar. - */ void -empathy_avatar_chooser_get_image_data (EmpathyAvatarChooser *self, - const gchar **data, - gsize *data_size, - const gchar **mime_type) +empathy_avatar_chooser_apply_async (EmpathyAvatarChooser *self, + GAsyncReadyCallback callback, + gpointer user_data) { - g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (self)); + GSimpleAsyncResult *result; - if (self->priv->avatar != NULL) - { - if (data != NULL) - *data = (gchar *) self->priv->avatar->data; + g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (self)); - if (data_size != NULL) - *data_size = self->priv->avatar->len; + result = g_simple_async_result_new ((GObject *) self, callback, user_data, + empathy_avatar_chooser_apply_async); - if (mime_type != NULL) - *mime_type = self->priv->avatar->format; - } - else + if (!self->priv->changed) { - if (data != NULL) - *data = NULL; + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + return; + } - if (data_size != NULL) - *data_size = 0; + self->priv->changed = FALSE; - if (mime_type != NULL) - *mime_type = NULL; - } + DEBUG ("%s Account.Avatar on %s", self->priv->avatar != NULL ? "Set": "Clear", + tp_proxy_get_object_path (self->priv->account)); + + tp_account_set_avatar_async (self->priv->account, + self->priv->avatar != NULL ? (guchar *) self->priv->avatar->data : NULL, + self->priv->avatar != NULL ? self->priv->avatar->len : 0, + self->priv->mime_type, set_avatar_cb, result); } -void -empathy_avatar_chooser_set_account (EmpathyAvatarChooser *self, - TpAccount *account) +gboolean +empathy_avatar_chooser_apply_finish (EmpathyAvatarChooser *self, + GAsyncResult *result, + GError **error) { - g_return_if_fail (account != NULL); - - avatar_chooser_set_connection (self, tp_account_get_connection (account)); - g_object_notify (G_OBJECT (self), "connection"); + empathy_implement_finish_void (self, empathy_avatar_chooser_apply_async); } diff --git a/libempathy-gtk/empathy-avatar-chooser.h b/libempathy-gtk/empathy-avatar-chooser.h index 7df142b64..fc21a7a39 100644 --- a/libempathy-gtk/empathy-avatar-chooser.h +++ b/libempathy-gtk/empathy-avatar-chooser.h @@ -55,17 +55,13 @@ struct _EmpathyAvatarChooserClass GType empathy_avatar_chooser_get_type (void); -GtkWidget *empathy_avatar_chooser_new (void); +GtkWidget *empathy_avatar_chooser_new (TpAccount *account); -void empathy_avatar_chooser_set (EmpathyAvatarChooser *self, - EmpathyAvatar *avatar); - -void empathy_avatar_chooser_get_image_data (EmpathyAvatarChooser *self, - const gchar **data, - gsize *data_size, - const gchar **mime_type); - -void empathy_avatar_chooser_set_account (EmpathyAvatarChooser *self, - TpAccount *account); +void empathy_avatar_chooser_apply_async (EmpathyAvatarChooser *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean empathy_avatar_chooser_apply_finish (EmpathyAvatarChooser *self, + GAsyncResult *result, + GError **error); #endif /* __EMPATHY_AVATAR_CHOOSER_H__ */ |