comparison 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
comparison
equal deleted inserted replaced
32611:7e6ab5abf620 32612:c317ae5dcb6e
148 148
149 wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); 149 wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
150 pidgin_window_init(wnd, title, border_width, role, resizable); 150 pidgin_window_init(wnd, title, border_width, role, resizable);
151 151
152 return GTK_WIDGET(wnd); 152 return GTK_WIDGET(wnd);
153 }
154
155 GtkWidget *
156 pidgin_create_small_button(GtkWidget *image)
157 {
158 GtkWidget *button;
159
160 button = gtk_button_new();
161 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
162
163 /* don't allow focus on the close button */
164 gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
165
166 /* set style to make it as small as possible */
167 gtk_widget_set_name(button, "pidgin-small-close-button");
168
169 gtk_widget_show(image);
170
171 gtk_container_add(GTK_CONTAINER(button), image);
172
173 return button;
153 } 174 }
154 175
155 GtkWidget * 176 GtkWidget *
156 pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable) 177 pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable)
157 { 178 {
2309 #endif 2330 #endif
2310 2331
2311 return dialog->icon_filesel; 2332 return dialog->icon_filesel;
2312 } 2333 }
2313 2334
2314 2335 /**
2336 * @return True if any string from array a exists in array b.
2337 */
2315 static gboolean 2338 static gboolean
2316 str_array_match(char **a, char **b) 2339 str_array_match(char **a, char **b)
2317 { 2340 {
2318 int i, j; 2341 int i, j;
2319 2342
2328 2351
2329 gpointer 2352 gpointer
2330 pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len) 2353 pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len)
2331 { 2354 {
2332 PurplePluginProtocolInfo *prpl_info; 2355 PurplePluginProtocolInfo *prpl_info;
2356 PurpleBuddyIconSpec *spec;
2357 int orig_width, orig_height, new_width, new_height;
2358 GdkPixbufFormat *format;
2359 char **pixbuf_formats;
2333 char **prpl_formats; 2360 char **prpl_formats;
2334 int width, height; 2361 GError *error = NULL;
2335 char **pixbuf_formats = NULL;
2336 GdkPixbufFormat *format;
2337 GdkPixbuf *pixbuf;
2338 gchar *contents; 2362 gchar *contents;
2339 gsize length; 2363 gsize length;
2364 GdkPixbuf *pixbuf, *original;
2365 float scale_factor;
2366 int i;
2367 gchar *tmp;
2340 2368
2341 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); 2369 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
2342 2370 spec = &prpl_info->icon_spec;
2343 g_return_val_if_fail(prpl_info->icon_spec.format != NULL, NULL); 2371 g_return_val_if_fail(spec->format != NULL, NULL);
2344 2372
2345 2373 format = gdk_pixbuf_get_file_info(path, &orig_width, &orig_height);
2346 format = gdk_pixbuf_get_file_info(path, &width, &height); 2374 if (format == NULL) {
2347 2375 purple_debug_warning("buddyicon", "Could not get file info of %s\n", path);
2348 if (format == NULL)
2349 return NULL; 2376 return NULL;
2377 }
2350 2378
2351 pixbuf_formats = gdk_pixbuf_format_get_extensions(format); 2379 pixbuf_formats = gdk_pixbuf_format_get_extensions(format);
2352 prpl_formats = g_strsplit(prpl_info->icon_spec.format,",",0); 2380 prpl_formats = g_strsplit(spec->format, ",", 0);
2353 if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */ 2381
2354 (!(prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_SEND) || /* The prpl doesn't scale before it sends OR */ 2382 if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */
2355 (prpl_info->icon_spec.min_width <= width && 2383 (!(spec->scale_rules & PURPLE_ICON_SCALE_SEND) || /* The prpl doesn't scale before it sends OR */
2356 prpl_info->icon_spec.max_width >= width && 2384 (spec->min_width <= orig_width && spec->max_width >= orig_width &&
2357 prpl_info->icon_spec.min_height <= height && 2385 spec->min_height <= orig_height && spec->max_height >= orig_height))) /* The icon is the correct size */
2358 prpl_info->icon_spec.max_height >= height))) /* The icon is the correct size */
2359 { 2386 {
2360 g_strfreev(prpl_formats);
2361 g_strfreev(pixbuf_formats); 2387 g_strfreev(pixbuf_formats);
2362 2388
2363 /* We don't need to scale the image. */ 2389 if (!g_file_get_contents(path, &contents, &length, &error)) {
2364 contents = NULL; 2390 purple_debug_warning("buddyicon", "Could not get file contents "
2365 if (!g_file_get_contents(path, &contents, &length, NULL)) 2391 "of %s: %s\n", path, error->message);
2366 {
2367 g_free(contents);
2368 return NULL;
2369 }
2370 }
2371 else
2372 {
2373 int i;
2374 GError *error = NULL;
2375 GdkPixbuf *scale;
2376 gboolean success = FALSE;
2377 char *filename = NULL;
2378
2379 g_strfreev(pixbuf_formats);
2380
2381 pixbuf = gdk_pixbuf_new_from_file(path, &error);
2382 if (error) {
2383 purple_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message);
2384 g_error_free(error);
2385 g_strfreev(prpl_formats); 2392 g_strfreev(prpl_formats);
2386 return NULL; 2393 return NULL;
2387 } 2394 }
2388 2395
2389 if ((prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_SEND) && 2396 if (spec->max_filesize == 0 || length < spec->max_filesize) {
2390 (width < prpl_info->icon_spec.min_width || 2397 /* The supplied image fits the file size, dimensions and type
2391 width > prpl_info->icon_spec.max_width || 2398 constraints. Great! Return it without making any changes. */
2392 height < prpl_info->icon_spec.min_height || 2399 if (len)
2393 height > prpl_info->icon_spec.max_height)) 2400 *len = length;
2394 { 2401 g_strfreev(prpl_formats);
2395 int new_width = width; 2402 return contents;
2396 int new_height = height; 2403 }
2397 2404
2398 purple_buddy_icon_get_scale_size(&prpl_info->icon_spec, &new_width, &new_height); 2405 /* The image was too big. Fall-through and try scaling it down. */
2399 2406 g_free(contents);
2400 scale = gdk_pixbuf_scale_simple(pixbuf, new_width, new_height, 2407 } else {
2401 GDK_INTERP_HYPER); 2408 g_strfreev(pixbuf_formats);
2402 g_object_unref(G_OBJECT(pixbuf)); 2409 }
2403 pixbuf = scale; 2410
2404 } 2411 /* The original image wasn't compatible. Scale it or convert file type. */
2405 2412 pixbuf = gdk_pixbuf_new_from_file(path, &error);
2413 if (error) {
2414 purple_debug_warning("buddyicon", "Could not open icon '%s' for "
2415 "conversion: %s\n", path, error->message);
2416 g_error_free(error);
2417 g_strfreev(prpl_formats);
2418 return NULL;
2419 }
2420 original = g_object_ref(G_OBJECT(pixbuf));
2421
2422 new_width = orig_width;
2423 new_height = orig_height;
2424
2425 /* Make sure the image is the correct dimensions */
2426 if (spec->scale_rules & PURPLE_ICON_SCALE_SEND &&
2427 (orig_width < spec->min_width || orig_width > spec->max_width ||
2428 orig_height < spec->min_height || orig_height > spec->max_height))
2429 {
2430 purple_buddy_icon_get_scale_size(spec, &new_width, &new_height);
2431
2432 g_object_unref(G_OBJECT(pixbuf));
2433 pixbuf = gdk_pixbuf_scale_simple(original, new_width, new_height, GDK_INTERP_HYPER);
2434 }
2435
2436 scale_factor = 1;
2437 do {
2406 for (i = 0; prpl_formats[i]; i++) { 2438 for (i = 0; prpl_formats[i]; i++) {
2407 FILE *fp; 2439 int quality = 100;
2408 2440 do {
2409 g_free(filename); 2441 const char *key = NULL;
2410 fp = purple_mkstemp(&filename, TRUE); 2442 const char *value = NULL;
2411 if (!fp) 2443 gchar tmp_buf[4];
2412 { 2444
2413 g_free(filename); 2445 purple_debug_info("buddyicon", "Converting buddy icon to %s\n", prpl_formats[i]);
2414 return NULL; 2446
2415 } 2447 if (g_str_equal(prpl_formats[i], "png")) {
2416 fclose(fp); 2448 key = "compression";
2417 2449 value = "9";
2418 purple_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename); 2450 } else if (g_str_equal(prpl_formats[i], "jpeg")) {
2419 /* The "compression" param wasn't supported until gdk-pixbuf 2.8. 2451 sprintf(tmp_buf, "%u", quality);
2420 * Using it in previous versions causes the save to fail (and an assert message). */ 2452 key = "quality";
2421 if ((gdk_pixbuf_major_version > 2 || (gdk_pixbuf_major_version == 2 2453 value = tmp_buf;
2422 && gdk_pixbuf_minor_version >= 8)) 2454 }
2423 && strcmp(prpl_formats[i], "png") == 0) { 2455
2424 if (gdk_pixbuf_save(pixbuf, filename, prpl_formats[i], 2456 if (!gdk_pixbuf_save_to_buffer(pixbuf, &contents, &length,
2425 &error, "compression", "9", NULL)) { 2457 prpl_formats[i], &error, key, value, NULL))
2426 success = TRUE; 2458 {
2459 /* The NULL checking of error is necessary due to this bug:
2460 * http://bugzilla.gnome.org/show_bug.cgi?id=405539 */
2461 purple_debug_warning("buddyicon",
2462 "Could not convert to %s: %s\n", prpl_formats[i],
2463 (error && error->message) ? error->message : "Unknown error");
2464 g_error_free(error);
2465 error = NULL;
2466
2467 /* We couldn't convert to this image type. Try the next
2468 image type. */
2427 break; 2469 break;
2428 } 2470 }
2429 } else if (gdk_pixbuf_save(pixbuf, filename, prpl_formats[i], 2471
2430 &error, NULL)) { 2472 if (spec->max_filesize == 0 || length <= spec->max_filesize) {
2431 success = TRUE; 2473 /* We were able to save the image as this image type and
2432 break; 2474 have it be within the size constraints. Great! Return
2433 } 2475 the image. */
2434 2476 purple_debug_info("buddyicon", "Converted image from "
2435 /* The NULL checking is necessary due to this bug: 2477 "%dx%d to %dx%d, format=%s, quality=%u, "
2436 * http://bugzilla.gnome.org/show_bug.cgi?id=405539 */ 2478 "filesize=%zu\n", orig_width, orig_height,
2437 purple_debug_warning("buddyicon", "Could not convert to %s: %s\n", prpl_formats[i], 2479 new_width, new_height, prpl_formats[i], quality,
2438 (error && error->message) ? error->message : "Unknown error"); 2480 length);
2439 g_error_free(error); 2481 if (len)
2440 error = NULL; 2482 *len = length;
2441 } 2483 g_strfreev(prpl_formats);
2442 g_strfreev(prpl_formats); 2484 g_object_unref(G_OBJECT(pixbuf));
2485 g_object_unref(G_OBJECT(original));
2486 return contents;
2487 }
2488
2489 g_free(contents);
2490
2491 if (!g_str_equal(prpl_formats[i], "jpeg")) {
2492 /* File size was too big and we can't lower the quality,
2493 so skip to the next image type. */
2494 break;
2495 }
2496
2497 /* File size was too big, but we're dealing with jpeg so try
2498 lowering the quality. */
2499 quality -= 5;
2500 } while (quality >= 70);
2501 }
2502
2503 /* We couldn't save the image in any format that was below the max
2504 file size. Maybe we can reduce the image dimensions? */
2505 scale_factor *= 0.8;
2506 new_width = orig_width * scale_factor;
2507 new_height = orig_height * scale_factor;
2443 g_object_unref(G_OBJECT(pixbuf)); 2508 g_object_unref(G_OBJECT(pixbuf));
2444 if (!success) { 2509 pixbuf = gdk_pixbuf_scale_simple(original, new_width, new_height, GDK_INTERP_HYPER);
2445 purple_debug_error("buddyicon", "Could not convert icon to usable format.\n"); 2510 } while ((new_width > 10 || new_height > 10) && new_width > spec->min_width && new_height > spec->min_height);
2446 g_free(filename); 2511 g_strfreev(prpl_formats);
2447 return NULL; 2512 g_object_unref(G_OBJECT(pixbuf));
2448 } 2513 g_object_unref(G_OBJECT(original));
2449 2514
2450 contents = NULL; 2515 tmp = g_strdup_printf(_("The file '%s' is too large for %s. Please try a smaller image.\n"),
2451 if (!g_file_get_contents(filename, &contents, &length, NULL)) 2516 path, plugin->info->name);
2452 { 2517 purple_notify_error(NULL, _("Icon Error"), _("Could not set icon"), tmp);
2453 purple_debug_error("buddyicon", 2518 g_free(tmp);
2454 "Could not read '%s', which we just wrote to disk.\n", 2519
2455 filename); 2520 return NULL;
2456
2457 g_free(contents);
2458 g_free(filename);
2459 return NULL;
2460 }
2461
2462 g_unlink(filename);
2463 g_free(filename);
2464 }
2465
2466 /* Check the image size */
2467 /*
2468 * TODO: If the file is too big, it would be cool if we checked if
2469 * the prpl supported jpeg, and then we could convert to that
2470 * and use a lower quality setting.
2471 */
2472 if ((prpl_info->icon_spec.max_filesize != 0) &&
2473 (length > prpl_info->icon_spec.max_filesize))
2474 {
2475 gchar *tmp;
2476 tmp = g_strdup_printf(_("The file '%s' is too large for %s. Please try a smaller image.\n"),
2477 path, plugin->info->name);
2478 purple_notify_error(NULL, _("Icon Error"),
2479 _("Could not set icon"), tmp);
2480 purple_debug_info("buddyicon",
2481 "'%s' was converted to an image which is %" G_GSIZE_FORMAT
2482 " bytes, but the maximum icon size for %s is %" G_GSIZE_FORMAT
2483 " bytes\n", path, length, plugin->info->name,
2484 prpl_info->icon_spec.max_filesize);
2485 g_free(tmp);
2486 return NULL;
2487 }
2488
2489 if (len)
2490 *len = length;
2491 return contents;
2492 } 2521 }
2493 2522
2494 void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename) 2523 void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename)
2495 { 2524 {
2496 PurpleBuddy *buddy; 2525 PurpleBuddy *buddy;
3446 3475
3447 /* If we're under GNOME, try registering the system URL handlers. */ 3476 /* If we're under GNOME, try registering the system URL handlers. */
3448 if (purple_running_gnome()) 3477 if (purple_running_gnome())
3449 register_gnome_url_handlers(); 3478 register_gnome_url_handlers();
3450 3479
3480 /* Used to make small buttons */
3481 gtk_rc_parse_string("style \"pidgin-small-close-button\"\n"
3482 "{\n"
3483 "GtkWidget::focus-padding = 0\n"
3484 "GtkWidget::focus-line-width = 0\n"
3485 "xthickness = 0\n"
3486 "ythickness = 0\n"
3487 "GtkContainer::border-width = 0\n"
3488 "GtkButton::inner-border = {0, 0, 0, 0}\n"
3489 "GtkButton::default-border = {0, 0, 0, 0}\n"
3490 "}\n"
3491 "widget \"*.pidgin-small-close-button\" style \"pidgin-small-close-button\"");
3492
3451 #ifdef _WIN32 3493 #ifdef _WIN32
3452 winpidgin_register_win32_url_handlers(); 3494 winpidgin_register_win32_url_handlers();
3453 #endif 3495 #endif
3454 3496
3455 } 3497 }