diff options
-rw-r--r-- | libempathy-gtk/empathy-avatar-chooser.c | 330 |
1 files changed, 265 insertions, 65 deletions
diff --git a/libempathy-gtk/empathy-avatar-chooser.c b/libempathy-gtk/empathy-avatar-chooser.c index 72f3d0970..670cbd7b1 100644 --- a/libempathy-gtk/empathy-avatar-chooser.c +++ b/libempathy-gtk/empathy-avatar-chooser.c @@ -49,15 +49,13 @@ typedef struct { gulong ready_handler_id; - gchar *image_data; - gsize image_data_size; - gchar *mime_type; + EmpathyAvatar *avatar; } EmpathyAvatarChooserPriv; static void avatar_chooser_finalize (GObject *object); static void avatar_chooser_set_account (EmpathyAvatarChooser *self, McAccount *account); -static void avatar_chooser_set_image_from_data (EmpathyAvatarChooser *chooser, +static void avatar_chooser_set_image (EmpathyAvatarChooser *chooser, gchar *data, gsize size, gboolean set_locally); @@ -254,8 +252,9 @@ avatar_chooser_finalize (GObject *object) avatar_chooser_set_account (EMPATHY_AVATAR_CHOOSER (object), NULL); g_assert (priv->account == NULL && priv->tp_contact_factory == NULL); - g_free (priv->image_data); - g_free (priv->mime_type); + if (priv->avatar != NULL) { + empathy_avatar_unref (priv->avatar); + } G_OBJECT_CLASS (empathy_avatar_chooser_parent_class)->finalize (object); } @@ -310,47 +309,248 @@ avatar_chooser_set_account (EmpathyAvatarChooser *self, } -static void -avatar_chooser_set_image (EmpathyAvatarChooser *chooser, - GdkPixbuf *pixbuf, - gchar *image_data, - gsize image_data_size, - gchar *mime_type, - gboolean set_locally) +static gboolean +str_in_strv (gchar *str, + gchar **strv) { - EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser); - GtkWidget *image; - GdkPixbuf *pixbuf_view = NULL; + if (strv == NULL) + return FALSE; + while (*strv != NULL) + { + if (g_str_equal (str, *strv)) + return TRUE; + strv++; + } + return FALSE; +} - g_free (priv->image_data); - g_free (priv->mime_type); - priv->image_data = NULL; - priv->image_data_size = 0; - priv->mime_type = NULL; +/* The caller must free the strings stored in satisfactory_format_name and + * satisfactory_mime_type. + */ +static gboolean +can_satisfy_mime_type_requirements (gchar **accepted_mime_types, + gchar **satisfactory_format_name, + gchar **satisfactory_mime_type) +{ + GSList *formats; + GSList *i; + gchar **j; + gboolean done = FALSE; + + if (accepted_mime_types == NULL || *accepted_mime_types == NULL) + return FALSE; + + g_assert (satisfactory_format_name != NULL); + g_assert (satisfactory_mime_type != NULL); + + formats = gdk_pixbuf_get_formats (); + + for (i = formats; !done && i != NULL; i = i->next) { + GdkPixbufFormat *format = i->data; + gchar **format_mime_types; - if (pixbuf) { - priv->image_data = image_data; - priv->image_data_size = image_data_size; - priv->mime_type = mime_type; + if (!gdk_pixbuf_format_is_writable (format)) + continue; - pixbuf_view = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_VIEW); - image = gtk_image_new_from_pixbuf (pixbuf_view); - g_object_unref (pixbuf_view); + format_mime_types = gdk_pixbuf_format_get_mime_types (format); + for (j = accepted_mime_types; *j != NULL; j++) { + if (str_in_strv (*j, format_mime_types)) { + *satisfactory_format_name = gdk_pixbuf_format_get_name (format); + *satisfactory_mime_type = g_strdup (*j); + done = TRUE; + break; + } + } + g_strfreev (format_mime_types); + } + + g_slist_free (formats); + + return done; +} + + +static EmpathyAvatar * +avatar_chooser_convert (EmpathyAvatarChooser *chooser, + GdkPixbuf *pixbuf_scaled, + gchar **mime_types, + gsize max_size) +{ + gchar *format_name = NULL, *new_mime_type = NULL; + + gchar *converted_image_data = NULL; + gsize converted_image_size = 0; + EmpathyAvatar *converted_avatar = NULL; + + gboolean saved; + GError *error = NULL; + + if (!can_satisfy_mime_type_requirements (mime_types, &format_name, + &new_mime_type)) { + DEBUG ("Mon dieu! Can't convert to any acceptable format!"); + return NULL; + } + + saved = gdk_pixbuf_save_to_buffer (pixbuf_scaled, &converted_image_data, + &converted_image_size, format_name, &error, NULL); + g_free (format_name); + + if (!saved) { + DEBUG ("Couldn't convert image: %s", error->message); + g_error_free (error); + + g_free (new_mime_type); + return NULL; + } + + /* Takes ownership of new_mime_type */ + converted_avatar = empathy_avatar_new (converted_image_data, + converted_image_size, new_mime_type, NULL); + + if (max_size > 0 && converted_avatar->len > max_size) { + /* TODO: We could try converting to a different format; in + * particular, try converting to jpeg with increasingly + * high compression (if jpeg is supported). Not sure how + * much we care. + */ + DEBUG ("Converted the image, but the new filesize is too big"); + + empathy_avatar_unref (converted_avatar); + converted_avatar = NULL; + } + + return converted_avatar; +} + + +static EmpathyAvatar * +avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *chooser, + GdkPixbuf *pixbuf, + EmpathyAvatar *avatar) +{ + EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser); + EmpathyTpContactFactory *tp_cf = priv->tp_contact_factory; + + gint max_width = 0, max_height = 0, max_size = 0; + gchar **mime_types = NULL; + gboolean needs_conversion = FALSE; + + GdkPixbuf *pixbuf_scaled = NULL; + + /* This should only be called if the user is setting a new avatar, + * which should only be allowed once the avatar requirements have been + * discovered. + */ + g_return_val_if_fail (tp_cf != NULL, NULL); + g_return_val_if_fail (empathy_tp_contact_factory_is_ready (tp_cf), + NULL); + + g_object_get (tp_cf, + "avatar-mime-types", &mime_types, /* Needs g_strfreev-ing */ + "avatar-max-width", &max_width, + "avatar-max-height", &max_height, + "avatar-max-size", &max_size, + NULL); + + /* If the avatar's not already the right type, it needs converting. */ + if (!str_in_strv (avatar->format, mime_types)) + needs_conversion = TRUE; + + /* If scaling down the pixbuf to fit the dimensions yields a new + * pixbuf, then it needed scaling. if it's the same pixbuf, it did not. + */ + pixbuf_scaled = empathy_pixbuf_scale_down_if_necessary (pixbuf, + MIN(max_width, max_height)); + if (pixbuf_scaled != pixbuf) + needs_conversion = TRUE; + + if (max_size > 0 && avatar->len > max_size) + needs_conversion = TRUE; + + if (needs_conversion) { + avatar = avatar_chooser_convert (chooser, pixbuf_scaled, + mime_types, max_size); } else { - image = gtk_image_new_from_icon_name ("stock_person", - GTK_ICON_SIZE_DIALOG); + /* Just return another reference to the avatar passed in. */ + avatar = empathy_avatar_ref (avatar); } + g_object_unref (pixbuf_scaled); + g_strfreev (mime_types); + return avatar; +} + + +static void +avatar_chooser_clear_image (EmpathyAvatarChooser *chooser) +{ + EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser); + GtkWidget *image; + + if (priv->avatar == NULL) + return; + + empathy_avatar_unref (priv->avatar); + priv->avatar = NULL; + + image = gtk_image_new_from_icon_name ("stock_person", GTK_ICON_SIZE_DIALOG); gtk_button_set_image (GTK_BUTTON (chooser), image); g_signal_emit (chooser, signals[CHANGED], 0); } static void -avatar_chooser_clear_image (EmpathyAvatarChooser *chooser, - gboolean set_locally) +avatar_chooser_set_image (EmpathyAvatarChooser *chooser, + gchar *data, + gsize size, + gboolean set_locally) { - avatar_chooser_set_image (chooser, NULL, NULL, 0, NULL, set_locally); + EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser); + + GdkPixbuf *pixbuf, *pixbuf_view; + GtkWidget *image; + EmpathyAvatar *avatar = NULL; + gchar *mime_type = NULL; + + if (data == NULL || size == 0) { + avatar_chooser_clear_image (chooser); + g_free (data); + return; + } + + pixbuf = empathy_pixbuf_from_data (data, size, &mime_type); + if (pixbuf == NULL) { + g_free (data); + return; + } + + /* avatar takes ownership of data and mime_type */ + avatar = empathy_avatar_new (data, size, mime_type, NULL); + + if (set_locally) { + EmpathyAvatar *conv = avatar_chooser_maybe_convert_and_scale ( + chooser, pixbuf, avatar); + empathy_avatar_unref (avatar); + avatar = conv; + } + + if (avatar == NULL) { + /* An error occured; don't change the avatar. */ + return; + } + + if (priv->avatar != NULL) + empathy_avatar_unref (priv->avatar); + priv->avatar = avatar; + + 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 (chooser), image); + g_signal_emit (chooser, signals[CHANGED], 0); + + g_object_unref (pixbuf_view); + g_object_unref (pixbuf); } static void @@ -371,23 +571,7 @@ avatar_chooser_set_image_from_file (EmpathyAvatarChooser *chooser, return; } - avatar_chooser_set_image_from_data (chooser, image_data, image_size, TRUE); -} - -static void -avatar_chooser_set_image_from_data (EmpathyAvatarChooser *chooser, - gchar *data, - gsize size, - gboolean set_locally) -{ - GdkPixbuf *pixbuf; - gchar *mime_type = NULL; - - pixbuf = empathy_pixbuf_from_data (data, size, &mime_type); - avatar_chooser_set_image (chooser, pixbuf, data, size, mime_type, set_locally); - if (pixbuf) { - g_object_unref (pixbuf); - } + avatar_chooser_set_image (chooser, image_data, image_size, TRUE); } static gboolean @@ -517,10 +701,10 @@ avatar_chooser_drag_data_received_cb (GtkWidget *widget, data, size, NULL, NULL); if (bytes_read != -1) { - avatar_chooser_set_image_from_data (chooser, - data, - (gsize) bytes_read, - TRUE); + avatar_chooser_set_image (chooser, + data, + (gsize) bytes_read, + TRUE); handled = TRUE; } @@ -591,7 +775,8 @@ avatar_chooser_response_cb (GtkWidget *widget, } } else if (response == GTK_RESPONSE_NO) { - avatar_chooser_clear_image (chooser, TRUE); + /* This corresponds to "No Image", not to "Cancel" */ + avatar_chooser_clear_image (chooser); } gtk_widget_destroy (widget); @@ -698,6 +883,9 @@ empathy_avatar_chooser_new (EmpathyContactFactory *contact_factory) NULL); } +/* TODO: when the avatar passed to this function actually can be relied upon to + * contain a mime type, we can probably just ref it and store it. + */ void empathy_avatar_chooser_set (EmpathyAvatarChooser *chooser, EmpathyAvatar *avatar) @@ -706,9 +894,9 @@ empathy_avatar_chooser_set (EmpathyAvatarChooser *chooser, if (avatar != NULL) { gchar *data = g_memdup (avatar->data, avatar->len); - avatar_chooser_set_image_from_data (chooser, data, avatar->len, FALSE); + avatar_chooser_set_image (chooser, data, avatar->len, FALSE); } else { - avatar_chooser_clear_image (chooser, FALSE); + avatar_chooser_clear_image (chooser); } } @@ -724,14 +912,26 @@ empathy_avatar_chooser_get_image_data (EmpathyAvatarChooser *chooser, priv = GET_PRIV (chooser); - if (data) { - *data = priv->image_data; - } - if (data_size) { - *data_size = priv->image_data_size; - } - if (mime_type) { - *mime_type = priv->mime_type; + if (priv->avatar != NULL) { + if (data != NULL) { + *data = priv->avatar->data; + } + if (data_size != NULL) { + *data_size = priv->avatar->len; + } + if (mime_type != NULL) { + *mime_type = priv->avatar->format; + } + } else { + if (data != NULL) { + *data = NULL; + } + if (data_size != NULL) { + *data_size = 0; + } + if (mime_type != NULL) { + *mime_type = NULL; + } } } |