Mercurial > pidgin.yaz
comparison pidgin/gtkutils.c @ 30456:31f20c9c7674
merge of '21280175da42b51e30e31b091bb3b4adf7708407'
and 'de0f6b7b9429dd7161de925ce6bdb02fbb7daaec'
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Fri, 23 Apr 2010 22:19:23 +0000 |
parents | 9e9c20b705d3 |
children | 06fa97f637a7 c62fac7ada0d 7c871249318b |
comparison
equal
deleted
inserted
replaced
30455:e47a4a7940ea | 30456:31f20c9c7674 |
---|---|
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 } |