Mercurial > pidgin
diff pidgin/gtkutils.c @ 32612:c317ae5dcb6e
propagate from branch 'im.pidgin.pidgin' (head 52f4817b6a7b2db5cad41758c4911c0f20f77eb2)
to branch 'im.pidgin.cpw.qulogic.gtk3' (head 09d4e9b50dd8919f186c2221e24b5f388bec7a88)
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Sat, 05 Jun 2010 21:22:26 +0000 |
parents | 2cbccbdf479a 9e9c20b705d3 |
children |
line wrap: on
line diff
--- a/pidgin/gtkutils.c Sat Feb 20 07:03:22 2010 +0000 +++ b/pidgin/gtkutils.c Sat Jun 05 21:22:26 2010 +0000 @@ -153,6 +153,27 @@ } GtkWidget * +pidgin_create_small_button(GtkWidget *image) +{ + GtkWidget *button; + + button = gtk_button_new(); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + + /* don't allow focus on the close button */ + gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); + + /* set style to make it as small as possible */ + gtk_widget_set_name(button, "pidgin-small-close-button"); + + gtk_widget_show(image); + + gtk_container_add(GTK_CONTAINER(button), image); + + return button; +} + +GtkWidget * pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable) { GtkWindow *wnd = NULL; @@ -2311,7 +2332,9 @@ return dialog->icon_filesel; } - +/** + * @return True if any string from array a exists in array b. + */ static gboolean str_array_match(char **a, char **b) { @@ -2330,165 +2353,171 @@ pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len) { PurplePluginProtocolInfo *prpl_info; + PurpleBuddyIconSpec *spec; + int orig_width, orig_height, new_width, new_height; + GdkPixbufFormat *format; + char **pixbuf_formats; char **prpl_formats; - int width, height; - char **pixbuf_formats = NULL; - GdkPixbufFormat *format; - GdkPixbuf *pixbuf; + GError *error = NULL; gchar *contents; gsize length; + GdkPixbuf *pixbuf, *original; + float scale_factor; + int i; + gchar *tmp; prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - - g_return_val_if_fail(prpl_info->icon_spec.format != NULL, NULL); - - - format = gdk_pixbuf_get_file_info(path, &width, &height); - - if (format == NULL) + spec = &prpl_info->icon_spec; + g_return_val_if_fail(spec->format != NULL, NULL); + + format = gdk_pixbuf_get_file_info(path, &orig_width, &orig_height); + if (format == NULL) { + purple_debug_warning("buddyicon", "Could not get file info of %s\n", path); return NULL; + } pixbuf_formats = gdk_pixbuf_format_get_extensions(format); - prpl_formats = g_strsplit(prpl_info->icon_spec.format,",",0); - if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */ - (!(prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_SEND) || /* The prpl doesn't scale before it sends OR */ - (prpl_info->icon_spec.min_width <= width && - prpl_info->icon_spec.max_width >= width && - prpl_info->icon_spec.min_height <= height && - prpl_info->icon_spec.max_height >= height))) /* The icon is the correct size */ + prpl_formats = g_strsplit(spec->format, ",", 0); + + if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */ + (!(spec->scale_rules & PURPLE_ICON_SCALE_SEND) || /* The prpl doesn't scale before it sends OR */ + (spec->min_width <= orig_width && spec->max_width >= orig_width && + spec->min_height <= orig_height && spec->max_height >= orig_height))) /* The icon is the correct size */ { - g_strfreev(prpl_formats); g_strfreev(pixbuf_formats); - /* We don't need to scale the image. */ - contents = NULL; - if (!g_file_get_contents(path, &contents, &length, NULL)) - { - g_free(contents); - return NULL; - } - } - else - { - int i; - GError *error = NULL; - GdkPixbuf *scale; - gboolean success = FALSE; - char *filename = NULL; - - g_strfreev(pixbuf_formats); - - pixbuf = gdk_pixbuf_new_from_file(path, &error); - if (error) { - purple_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message); - g_error_free(error); + if (!g_file_get_contents(path, &contents, &length, &error)) { + purple_debug_warning("buddyicon", "Could not get file contents " + "of %s: %s\n", path, error->message); g_strfreev(prpl_formats); return NULL; } - if ((prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_SEND) && - (width < prpl_info->icon_spec.min_width || - width > prpl_info->icon_spec.max_width || - height < prpl_info->icon_spec.min_height || - height > prpl_info->icon_spec.max_height)) - { - int new_width = width; - int new_height = height; - - purple_buddy_icon_get_scale_size(&prpl_info->icon_spec, &new_width, &new_height); - - scale = gdk_pixbuf_scale_simple(pixbuf, new_width, new_height, - GDK_INTERP_HYPER); - g_object_unref(G_OBJECT(pixbuf)); - pixbuf = scale; + if (spec->max_filesize == 0 || length < spec->max_filesize) { + /* The supplied image fits the file size, dimensions and type + constraints. Great! Return it without making any changes. */ + if (len) + *len = length; + g_strfreev(prpl_formats); + return contents; } + /* The image was too big. Fall-through and try scaling it down. */ + g_free(contents); + } else { + g_strfreev(pixbuf_formats); + } + + /* The original image wasn't compatible. Scale it or convert file type. */ + pixbuf = gdk_pixbuf_new_from_file(path, &error); + if (error) { + purple_debug_warning("buddyicon", "Could not open icon '%s' for " + "conversion: %s\n", path, error->message); + g_error_free(error); + g_strfreev(prpl_formats); + return NULL; + } + original = g_object_ref(G_OBJECT(pixbuf)); + + new_width = orig_width; + new_height = orig_height; + + /* Make sure the image is the correct dimensions */ + if (spec->scale_rules & PURPLE_ICON_SCALE_SEND && + (orig_width < spec->min_width || orig_width > spec->max_width || + orig_height < spec->min_height || orig_height > spec->max_height)) + { + purple_buddy_icon_get_scale_size(spec, &new_width, &new_height); + + g_object_unref(G_OBJECT(pixbuf)); + pixbuf = gdk_pixbuf_scale_simple(original, new_width, new_height, GDK_INTERP_HYPER); + } + + scale_factor = 1; + do { for (i = 0; prpl_formats[i]; i++) { - FILE *fp; - - g_free(filename); - fp = purple_mkstemp(&filename, TRUE); - if (!fp) - { - g_free(filename); - return NULL; - } - fclose(fp); - - purple_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename); - /* The "compression" param wasn't supported until gdk-pixbuf 2.8. - * Using it in previous versions causes the save to fail (and an assert message). */ - if ((gdk_pixbuf_major_version > 2 || (gdk_pixbuf_major_version == 2 - && gdk_pixbuf_minor_version >= 8)) - && strcmp(prpl_formats[i], "png") == 0) { - if (gdk_pixbuf_save(pixbuf, filename, prpl_formats[i], - &error, "compression", "9", NULL)) { - success = TRUE; + int quality = 100; + do { + const char *key = NULL; + const char *value = NULL; + gchar tmp_buf[4]; + + purple_debug_info("buddyicon", "Converting buddy icon to %s\n", prpl_formats[i]); + + if (g_str_equal(prpl_formats[i], "png")) { + key = "compression"; + value = "9"; + } else if (g_str_equal(prpl_formats[i], "jpeg")) { + sprintf(tmp_buf, "%u", quality); + key = "quality"; + value = tmp_buf; + } + + if (!gdk_pixbuf_save_to_buffer(pixbuf, &contents, &length, + prpl_formats[i], &error, key, value, NULL)) + { + /* The NULL checking of error is necessary due to this bug: + * http://bugzilla.gnome.org/show_bug.cgi?id=405539 */ + purple_debug_warning("buddyicon", + "Could not convert to %s: %s\n", prpl_formats[i], + (error && error->message) ? error->message : "Unknown error"); + g_error_free(error); + error = NULL; + + /* We couldn't convert to this image type. Try the next + image type. */ break; } - } else if (gdk_pixbuf_save(pixbuf, filename, prpl_formats[i], - &error, NULL)) { - success = TRUE; - break; - } - - /* The NULL checking is necessary due to this bug: - * http://bugzilla.gnome.org/show_bug.cgi?id=405539 */ - purple_debug_warning("buddyicon", "Could not convert to %s: %s\n", prpl_formats[i], - (error && error->message) ? error->message : "Unknown error"); - g_error_free(error); - error = NULL; - } - g_strfreev(prpl_formats); - g_object_unref(G_OBJECT(pixbuf)); - if (!success) { - purple_debug_error("buddyicon", "Could not convert icon to usable format.\n"); - g_free(filename); - return NULL; - } - - contents = NULL; - if (!g_file_get_contents(filename, &contents, &length, NULL)) - { - purple_debug_error("buddyicon", - "Could not read '%s', which we just wrote to disk.\n", - filename); - - g_free(contents); - g_free(filename); - return NULL; + + if (spec->max_filesize == 0 || length <= spec->max_filesize) { + /* We were able to save the image as this image type and + have it be within the size constraints. Great! Return + the image. */ + purple_debug_info("buddyicon", "Converted image from " + "%dx%d to %dx%d, format=%s, quality=%u, " + "filesize=%zu\n", orig_width, orig_height, + new_width, new_height, prpl_formats[i], quality, + length); + if (len) + *len = length; + g_strfreev(prpl_formats); + g_object_unref(G_OBJECT(pixbuf)); + g_object_unref(G_OBJECT(original)); + return contents; + } + + g_free(contents); + + if (!g_str_equal(prpl_formats[i], "jpeg")) { + /* File size was too big and we can't lower the quality, + so skip to the next image type. */ + break; + } + + /* File size was too big, but we're dealing with jpeg so try + lowering the quality. */ + quality -= 5; + } while (quality >= 70); } - g_unlink(filename); - g_free(filename); - } - - /* Check the image size */ - /* - * TODO: If the file is too big, it would be cool if we checked if - * the prpl supported jpeg, and then we could convert to that - * and use a lower quality setting. - */ - if ((prpl_info->icon_spec.max_filesize != 0) && - (length > prpl_info->icon_spec.max_filesize)) - { - gchar *tmp; - tmp = g_strdup_printf(_("The file '%s' is too large for %s. Please try a smaller image.\n"), - path, plugin->info->name); - purple_notify_error(NULL, _("Icon Error"), - _("Could not set icon"), tmp); - purple_debug_info("buddyicon", - "'%s' was converted to an image which is %" G_GSIZE_FORMAT - " bytes, but the maximum icon size for %s is %" G_GSIZE_FORMAT - " bytes\n", path, length, plugin->info->name, - prpl_info->icon_spec.max_filesize); - g_free(tmp); - return NULL; - } - - if (len) - *len = length; - return contents; + /* We couldn't save the image in any format that was below the max + file size. Maybe we can reduce the image dimensions? */ + scale_factor *= 0.8; + new_width = orig_width * scale_factor; + new_height = orig_height * scale_factor; + g_object_unref(G_OBJECT(pixbuf)); + pixbuf = gdk_pixbuf_scale_simple(original, new_width, new_height, GDK_INTERP_HYPER); + } while ((new_width > 10 || new_height > 10) && new_width > spec->min_width && new_height > spec->min_height); + g_strfreev(prpl_formats); + g_object_unref(G_OBJECT(pixbuf)); + g_object_unref(G_OBJECT(original)); + + tmp = g_strdup_printf(_("The file '%s' is too large for %s. Please try a smaller image.\n"), + path, plugin->info->name); + purple_notify_error(NULL, _("Icon Error"), _("Could not set icon"), tmp); + g_free(tmp); + + return NULL; } void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename) @@ -3448,6 +3477,19 @@ if (purple_running_gnome()) register_gnome_url_handlers(); + /* Used to make small buttons */ + gtk_rc_parse_string("style \"pidgin-small-close-button\"\n" + "{\n" + "GtkWidget::focus-padding = 0\n" + "GtkWidget::focus-line-width = 0\n" + "xthickness = 0\n" + "ythickness = 0\n" + "GtkContainer::border-width = 0\n" + "GtkButton::inner-border = {0, 0, 0, 0}\n" + "GtkButton::default-border = {0, 0, 0, 0}\n" + "}\n" + "widget \"*.pidgin-small-close-button\" style \"pidgin-small-close-button\""); + #ifdef _WIN32 winpidgin_register_win32_url_handlers(); #endif