comparison pidgin/gtkimhtml.c @ 27842:acef4202e147

propagate from branch 'im.pidgin.pidgin' (head 217103c3e954ff2d295a6590ad3477d357894f9c) to branch 'im.pidgin.pidgin.yaz' (head 0a5a324e3974fba2b40dcd1bb82fb1cc8b8fcabf)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Sun, 25 May 2008 04:30:41 +0000
parents 22f8b5512ef4 88d67b1bb52a
children 30eaeb7cc076
comparison
equal deleted inserted replaced
27841:5fc417883a5d 27842:acef4202e147
30 #endif 30 #endif
31 31
32 #include "internal.h" 32 #include "internal.h"
33 #include "pidgin.h" 33 #include "pidgin.h"
34 #include "pidginstock.h" 34 #include "pidginstock.h"
35 #include "gtkutils.h"
36 #include "smiley.h"
37 #include "imgstore.h"
35 38
36 #include "debug.h" 39 #include "debug.h"
37 #include "util.h" 40 #include "util.h"
38 #include "gtkimhtml.h" 41 #include "gtkimhtml.h"
42 #include "gtksmiley.h"
39 #include "gtksourceiter.h" 43 #include "gtksourceiter.h"
40 #include "gtksourceundomanager.h" 44 #include "gtksourceundomanager.h"
41 #include "gtksourceview-marshal.h" 45 #include "gtksourceview-marshal.h"
42 #include <gtk/gtk.h> 46 #include <gtk/gtk.h>
43 #include <glib/gerror.h> 47 #include <glib/gerror.h>
393 397
394 /* Don't scroll here if we're in the middle of a smooth scroll */ 398 /* Don't scroll here if we're in the middle of a smooth scroll */
395 if (scroll && imhtml->scroll_time == NULL && 399 if (scroll && imhtml->scroll_time == NULL &&
396 GTK_WIDGET_REALIZED(imhtml)) 400 GTK_WIDGET_REALIZED(imhtml))
397 gtk_imhtml_scroll_to_end(imhtml, FALSE); 401 gtk_imhtml_scroll_to_end(imhtml, FALSE);
402 }
403
404 #define DEFAULT_SEND_COLOR "#204a87"
405 #define DEFAULT_RECV_COLOR "#cc0000"
406 #define DEFAULT_HIGHLIGHT_COLOR "#AF7F00"
407 #define DEFAULT_ACTION_COLOR "#062585"
408 #define DEFAULT_WHISPER_ACTION_COLOR "#6C2585"
409 #define DEFAULT_WHISPER_COLOR "#00FF00"
410
411 static void (*parent_style_set)(GtkWidget *widget, GtkStyle *prev_style);
412
413 static void
414 gtk_imhtml_style_set(GtkWidget *widget, GtkStyle *prev_style)
415 {
416 int i;
417 struct {
418 const char *tag;
419 const char *color;
420 const char *def;
421 } styles[] = {
422 {"send-name", "send-name-color", DEFAULT_SEND_COLOR},
423 {"receive-name", "receive-name-color", DEFAULT_RECV_COLOR},
424 {"highlight-name", "highlight-name-color", DEFAULT_HIGHLIGHT_COLOR},
425 {"action-name", "action-name-color", DEFAULT_ACTION_COLOR},
426 {"whisper-action-name", "whisper-action-name-color", DEFAULT_WHISPER_ACTION_COLOR},
427 {"whisper-name", "whisper-name-color", DEFAULT_WHISPER_COLOR},
428 {NULL, NULL, NULL}
429 };
430 GtkIMHtml *imhtml = GTK_IMHTML(widget);
431 GtkTextTagTable *table = gtk_text_buffer_get_tag_table(imhtml->text_buffer);
432
433 for (i = 0; styles[i].tag; i++) {
434 GdkColor *color = NULL;
435 GtkTextTag *tag = gtk_text_tag_table_lookup(table, styles[i].tag);
436 if (!tag) {
437 purple_debug_warning("gtkimhtml", "Cannot find tag '%s'. This should never happen. Please file a bug.\n", styles[i].tag);
438 continue;
439 }
440 gtk_widget_style_get(widget, styles[i].color, &color, NULL);
441 if (color) {
442 g_object_set(tag, "foreground-gdk", color, NULL);
443 gdk_color_free(color);
444 } else {
445 GdkColor defcolor;
446 gdk_color_parse(styles[i].def, &defcolor);
447 g_object_set(tag, "foreground-gdk", &defcolor, NULL);
448 }
449 }
450 parent_style_set(widget, prev_style);
398 } 451 }
399 452
400 static gint 453 static gint
401 gtk_imhtml_tip_paint (GtkIMHtml *imhtml) 454 gtk_imhtml_tip_paint (GtkIMHtml *imhtml)
402 { 455 {
1431 gobject_class->finalize = gtk_imhtml_finalize; 1484 gobject_class->finalize = gtk_imhtml_finalize;
1432 widget_class->drag_motion = gtk_text_view_drag_motion; 1485 widget_class->drag_motion = gtk_text_view_drag_motion;
1433 widget_class->expose_event = gtk_imhtml_expose_event; 1486 widget_class->expose_event = gtk_imhtml_expose_event;
1434 parent_size_allocate = widget_class->size_allocate; 1487 parent_size_allocate = widget_class->size_allocate;
1435 widget_class->size_allocate = gtk_imhtml_size_allocate; 1488 widget_class->size_allocate = gtk_imhtml_size_allocate;
1489 parent_style_set = widget_class->style_set;
1490 widget_class->style_set = gtk_imhtml_style_set;
1436 1491
1437 gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-color", 1492 gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-color",
1438 _("Hyperlink color"), 1493 _("Hyperlink color"),
1439 _("Color to draw hyperlinks."), 1494 _("Color to draw hyperlinks."),
1440 GDK_TYPE_COLOR, G_PARAM_READABLE)); 1495 GDK_TYPE_COLOR, G_PARAM_READABLE));
1456 GDK_TYPE_COLOR, G_PARAM_READABLE)); 1511 GDK_TYPE_COLOR, G_PARAM_READABLE));
1457 gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("action-name-color", 1512 gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("action-name-color",
1458 _("Action Message Name Color"), 1513 _("Action Message Name Color"),
1459 _("Color to draw the name of an action message."), 1514 _("Color to draw the name of an action message."),
1460 GDK_TYPE_COLOR, G_PARAM_READABLE)); 1515 GDK_TYPE_COLOR, G_PARAM_READABLE));
1516 gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("whisper-action-name-color",
1517 _("Action Message Name Color for Whispered Message"),
1518 _("Color to draw the name of an action message."),
1519 GDK_TYPE_COLOR, G_PARAM_READABLE));
1520 gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("whisper-name-color",
1521 _("Whisper Message Name Color"),
1522 _("Color to draw the name of an action message."),
1523 GDK_TYPE_COLOR, G_PARAM_READABLE));
1461 1524
1462 /* Customizable typing notification ... sort of. Example: 1525 /* Customizable typing notification ... sort of. Example:
1463 * GtkIMHtml::typing-notification-font = "monospace italic light 8.0" 1526 * GtkIMHtml::typing-notification-font = "monospace italic light 8.0"
1464 * GtkIMHtml::typing-notification-color = "#ff0000" 1527 * GtkIMHtml::typing-notification-color = "#ff0000"
1465 * GtkIMHtml::typing-notification-enable = 1 1528 * GtkIMHtml::typing-notification-enable = 1
1517 gtk_text_buffer_create_tag(imhtml->text_buffer, "STRIKE", "strikethrough", TRUE, NULL); 1580 gtk_text_buffer_create_tag(imhtml->text_buffer, "STRIKE", "strikethrough", TRUE, NULL);
1518 gtk_text_buffer_create_tag(imhtml->text_buffer, "SUB", "rise", -5000, NULL); 1581 gtk_text_buffer_create_tag(imhtml->text_buffer, "SUB", "rise", -5000, NULL);
1519 gtk_text_buffer_create_tag(imhtml->text_buffer, "SUP", "rise", 5000, NULL); 1582 gtk_text_buffer_create_tag(imhtml->text_buffer, "SUP", "rise", 5000, NULL);
1520 gtk_text_buffer_create_tag(imhtml->text_buffer, "PRE", "family", "Monospace", NULL); 1583 gtk_text_buffer_create_tag(imhtml->text_buffer, "PRE", "family", "Monospace", NULL);
1521 gtk_text_buffer_create_tag(imhtml->text_buffer, "search", "background", "#22ff00", "weight", "bold", NULL); 1584 gtk_text_buffer_create_tag(imhtml->text_buffer, "search", "background", "#22ff00", "weight", "bold", NULL);
1585 gtk_text_buffer_create_tag(imhtml->text_buffer, "comment", "weight", PANGO_WEIGHT_NORMAL,
1522 #if FALSE && GTK_CHECK_VERSION(2,10,10) 1586 #if FALSE && GTK_CHECK_VERSION(2,10,10)
1523 gtk_text_buffer_create_tag(imhtml->text_buffer, "comment", "invisible", FALSE, NULL); 1587 "invisible", FALSE,
1524 #endif 1588 #endif
1589 NULL);
1590
1591 gtk_text_buffer_create_tag(imhtml->text_buffer, "send-name", "weight", PANGO_WEIGHT_BOLD, NULL);
1592 gtk_text_buffer_create_tag(imhtml->text_buffer, "receive-name", "weight", PANGO_WEIGHT_BOLD, NULL);
1593 gtk_text_buffer_create_tag(imhtml->text_buffer, "highlight-name", "weight", PANGO_WEIGHT_BOLD, NULL);
1594 gtk_text_buffer_create_tag(imhtml->text_buffer, "action-name", "weight", PANGO_WEIGHT_BOLD, NULL);
1595 gtk_text_buffer_create_tag(imhtml->text_buffer, "whisper-action-name", "weight", PANGO_WEIGHT_BOLD, NULL);
1596 gtk_text_buffer_create_tag(imhtml->text_buffer, "whisper-name", "weight", PANGO_WEIGHT_BOLD, NULL);
1525 1597
1526 /* When hovering over a link, we show the hand cursor--elsewhere we show the plain ol' pointer cursor */ 1598 /* When hovering over a link, we show the hand cursor--elsewhere we show the plain ol' pointer cursor */
1527 imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2); 1599 imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2);
1528 imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR); 1600 imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
1529 imhtml->text_cursor = gdk_cursor_new (GDK_XTERM); 1601 imhtml->text_cursor = gdk_cursor_new (GDK_XTERM);
3061 wpos = g_snprintf (ws, len, "%s", tag); 3133 wpos = g_snprintf (ws, len, "%s", tag);
3062 gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL); 3134 gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL);
3063 #else 3135 #else
3064 if (imhtml->show_comments && !(options & GTK_IMHTML_NO_COMMENTS)) { 3136 if (imhtml->show_comments && !(options & GTK_IMHTML_NO_COMMENTS)) {
3065 wpos = g_snprintf (ws, len, "%s", tag); 3137 wpos = g_snprintf (ws, len, "%s", tag);
3066 gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos); 3138 gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL);
3067 } 3139 }
3068 #endif 3140 #endif
3069 ws[0] = '\0'; wpos = 0; 3141 ws[0] = '\0'; wpos = 0;
3070 3142
3071 /* NEW_BIT (NEW_COMMENT_BIT); */ 3143 /* NEW_BIT (NEW_COMMENT_BIT); */
3629 #endif /* FILECHOOSER */ 3701 #endif /* FILECHOOSER */
3630 3702
3631 gtk_widget_show(image->filesel); 3703 gtk_widget_show(image->filesel);
3632 } 3704 }
3633 3705
3706 static void
3707 gtk_imhtml_custom_smiley_save(GtkWidget *w, GtkIMHtmlImage *image)
3708 {
3709 /* Create an add dialog */
3710 PidginSmiley *editor = pidgin_smiley_edit(NULL, NULL);
3711 pidgin_smiley_editor_set_shortcut(editor, image->filename);
3712 pidgin_smiley_editor_set_image(editor, image->pixbuf);
3713 }
3714
3634 /* 3715 /*
3635 * So, um, AIM Direct IM lets you send any file, not just images. You can 3716 * So, um, AIM Direct IM lets you send any file, not just images. You can
3636 * just insert a sound or a file or whatever in a conversation. It's 3717 * just insert a sound or a file or whatever in a conversation. It's
3637 * basically like file transfer, except there is an icon to open the file 3718 * basically like file transfer, except there is an icon to open the file
3638 * embedded in the conversation. Someone should make the Purple core handle 3719 * embedded in the conversation. Someone should make the Purple core handle
3653 item = gtk_image_menu_item_new_with_mnemonic(text); 3734 item = gtk_image_menu_item_new_with_mnemonic(text);
3654 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); 3735 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
3655 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_image_save), image); 3736 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_image_save), image);
3656 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); 3737 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
3657 3738
3739 /* Add menu item for adding custom smiley to local smileys */
3740 /* we only add the menu if the image is of "custom smiley size"
3741 <= 96x96 pixels */
3742 if (image->width <= 96 && image->height <= 96) {
3743 text = g_strdup_printf(_("_Add Custom Smiley..."));
3744 img = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
3745 item = gtk_image_menu_item_new_with_mnemonic(text);
3746 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
3747 g_signal_connect(G_OBJECT(item), "activate",
3748 G_CALLBACK(gtk_imhtml_custom_smiley_save), image);
3749 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
3750 }
3751
3658 gtk_widget_show_all(menu); 3752 gtk_widget_show_all(menu);
3659 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3753 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
3660 event_button->button, event_button->time); 3754 event_button->button, event_button->time);
3661 3755
3662 g_free(text); 3756 g_free(text);
3674 static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley) 3768 static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley)
3675 { 3769 {
3676 GdkPixbufAnimation *anim = NULL; 3770 GdkPixbufAnimation *anim = NULL;
3677 GtkIMHtmlScalable *image = NULL; 3771 GtkIMHtmlScalable *image = NULL;
3678 gboolean ret; 3772 gboolean ret;
3679 3773
3680 if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3) 3774 if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3)
3681 return FALSE; 3775 return FALSE;
3682 3776
3683 anim = gtk_smiley_get_image(smiley); 3777 anim = gtk_smiley_get_image(smiley);
3684 if (!anim) 3778 if (!anim)
4910 return buf; 5004 return buf;
4911 } else if (strncmp(name, "FONT SIZE ", 10) == 0) { 5005 } else if (strncmp(name, "FONT SIZE ", 10) == 0) {
4912 g_snprintf(buf, sizeof(buf), "<font size=\"%s\">", &name[10]); 5006 g_snprintf(buf, sizeof(buf), "<font size=\"%s\">", &name[10]);
4913 return buf; 5007 return buf;
4914 } else { 5008 } else {
4915 return ""; 5009 char *str = buf;
5010 gboolean isset;
5011 int ivalue = 0;
5012 GdkColor *color = NULL;
5013 GObject *obj = G_OBJECT(tag);
5014 gboolean empty = TRUE;
5015
5016 str += g_snprintf(str, sizeof(buf) - (str - buf), "<span style='");
5017
5018 /* Weight */
5019 g_object_get(obj, "weight-set", &isset, "weight", &ivalue, NULL);
5020 if (isset) {
5021 const char *weight = "";
5022 if (ivalue >= PANGO_WEIGHT_ULTRABOLD)
5023 weight = "bolder";
5024 else if (ivalue >= PANGO_WEIGHT_BOLD)
5025 weight = "bold";
5026 else if (ivalue >= PANGO_WEIGHT_NORMAL)
5027 weight = "normal";
5028 else
5029 weight = "lighter";
5030
5031 str += g_snprintf(str, sizeof(buf) - (str - buf), "font-weight: %s;", weight);
5032 empty = FALSE;
5033 }
5034
5035 /* Foreground color */
5036 g_object_get(obj, "foreground-set", &isset, "foreground-gdk", &color, NULL);
5037 if (isset && color) {
5038 str += g_snprintf(str, sizeof(buf) - (str - buf),
5039 "color: #%02x%02x%02x;",
5040 color->red >> 8, color->green >> 8, color->blue >> 8);
5041 gdk_color_free(color);
5042 empty = FALSE;
5043 }
5044
5045 /* Background color */
5046 g_object_get(obj, "background-set", &isset, "background-gdk", &color, NULL);
5047 if (isset && color) {
5048 str += g_snprintf(str, sizeof(buf) - (str - buf),
5049 "background: #%02x%02x%02x;",
5050 color->red >> 8, color->green >> 8, color->blue >> 8);
5051 gdk_color_free(color);
5052 empty = FALSE;
5053 }
5054
5055 /* Underline */
5056 g_object_get(obj, "underline-set", &isset, "underline", &ivalue, NULL);
5057 if (isset) {
5058 switch (ivalue) {
5059 case PANGO_UNDERLINE_NONE:
5060 case PANGO_UNDERLINE_ERROR:
5061 break;
5062 default:
5063 str += g_snprintf(str, sizeof(buf) - (str - buf), "text-decoration: underline;");
5064 }
5065 }
5066
5067 g_snprintf(str, sizeof(buf) - (str - buf), "'>");
5068
5069 return (empty ? "" : buf);
4916 } 5070 }
4917 } 5071 }
4918 5072
4919 static const gchar *tag_to_html_end(GtkTextTag *tag) 5073 static const gchar *tag_to_html_end(GtkTextTag *tag)
4920 { 5074 {
4942 } else if (strncmp(name, "FONT FACE ", 10) == 0) { 5096 } else if (strncmp(name, "FONT FACE ", 10) == 0) {
4943 return "</font>"; 5097 return "</font>";
4944 } else if (strncmp(name, "FONT SIZE ", 10) == 0) { 5098 } else if (strncmp(name, "FONT SIZE ", 10) == 0) {
4945 return "</font>"; 5099 return "</font>";
4946 } else { 5100 } else {
5101 const char *props[] = {"weight-set", "foreground-set", "background-set",
5102 "size-set", "underline-set", NULL};
5103 int i;
5104 for (i = 0; props[i]; i++) {
5105 gboolean set = FALSE;
5106 g_object_get(G_OBJECT(tag), props[i], &set, NULL);
5107 if (set)
5108 return "</span>";
5109 }
5110
4947 return ""; 5111 return "";
4948 } 5112 }
4949 } 5113 }
4950 5114
4951 static gboolean tag_ends_here(GtkTextTag *tag, GtkTextIter *iter, GtkTextIter *niter) 5115 static gboolean tag_ends_here(GtkTextTag *tag, GtkTextIter *iter, GtkTextIter *niter)
5271 } 5435 }
5272 5436
5273 if (flags & PURPLE_CONNECTION_NO_IMAGES) 5437 if (flags & PURPLE_CONNECTION_NO_IMAGES)
5274 buttons &= ~GTK_IMHTML_IMAGE; 5438 buttons &= ~GTK_IMHTML_IMAGE;
5275 5439
5440 if (flags & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
5441 buttons |= GTK_IMHTML_CUSTOM_SMILEY;
5442 else
5443 buttons &= ~GTK_IMHTML_CUSTOM_SMILEY;
5444
5276 gtk_imhtml_set_format_functions(imhtml, buttons); 5445 gtk_imhtml_set_format_functions(imhtml, buttons);
5277 } 5446 }
5278 5447
5279 5448 /*******
5449 * GtkIMHtmlSmiley functions
5450 *******/
5451 static void gtk_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data)
5452 {
5453 GtkIMHtmlSmiley *smiley;
5454
5455 smiley = (GtkIMHtmlSmiley *)user_data;
5456 smiley->icon = gdk_pixbuf_loader_get_animation(loader);
5457
5458 if (smiley->icon)
5459 g_object_ref(G_OBJECT(smiley->icon));
5460 #ifdef DEBUG_CUSTOM_SMILEY
5461 purple_debug_info("custom-smiley", "gtk_custom_smiley_allocated(): got GdkPixbufAnimation %p for smiley '%s'\n", smiley->icon, smiley->smile);
5462 #endif
5463 }
5464
5465 static void gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data)
5466 {
5467 GtkIMHtmlSmiley *smiley;
5468 GtkWidget *icon = NULL;
5469 GtkTextChildAnchor *anchor = NULL;
5470 GSList *current = NULL;
5471
5472 smiley = (GtkIMHtmlSmiley *)user_data;
5473 if (!smiley->imhtml) {
5474 #ifdef DEBUG_CUSTOM_SMILEY
5475 purple_debug_error("custom-smiley", "gtk_custom_smiley_closed(): orphan smiley found: %p\n", smiley);
5476 #endif
5477 g_object_unref(G_OBJECT(loader));
5478 smiley->loader = NULL;
5479 return;
5480 }
5481
5482 for (current = smiley->anchors; current; current = g_slist_next(current)) {
5483
5484 icon = gtk_image_new_from_animation(smiley->icon);
5485
5486 #ifdef DEBUG_CUSTOM_SMILEY
5487 purple_debug_info("custom-smiley", "gtk_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n",
5488 icon, smiley->icon, smiley->smile);
5489 #endif
5490 if (icon) {
5491 GList *wids;
5492 gtk_widget_show(icon);
5493
5494 anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
5495 wids = gtk_text_child_anchor_get_widgets(anchor);
5496
5497 g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", purple_unescape_html(smiley->smile), g_free);
5498 g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley->smile), g_free);
5499
5500 if (smiley->imhtml) {
5501 if (wids) {
5502 GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data));
5503 g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL);
5504 g_list_free(children);
5505 gtk_container_add(GTK_CONTAINER(wids->data), icon);
5506 } else
5507 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->imhtml), icon, anchor);
5508 }
5509 g_list_free(wids);
5510 }
5511
5512 }
5513
5514 g_slist_free(smiley->anchors);
5515 smiley->anchors = NULL;
5516
5517 g_object_unref(G_OBJECT(loader));
5518 smiley->loader = NULL;
5519 }
5520
5521 static void
5522 gtk_custom_smiley_size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer data)
5523 {
5524 #define CUSTOM_SMILEY_SIZE 96 /* XXX: Should this be a theme setting? */
5525 if (width <= CUSTOM_SMILEY_SIZE && height <= CUSTOM_SMILEY_SIZE)
5526 return;
5527
5528 if (width >= height) {
5529 height = height * CUSTOM_SMILEY_SIZE / width;
5530 width = CUSTOM_SMILEY_SIZE;
5531 } else {
5532 width = width * CUSTOM_SMILEY_SIZE / height;
5533 height = CUSTOM_SMILEY_SIZE;
5534 }
5535
5536 gdk_pixbuf_loader_set_size(loader, width, height);
5537 }
5538
5539 void
5540 gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley)
5541 {
5542 if (smiley->icon)
5543 g_object_unref(smiley->icon);
5544 if (smiley->loader)
5545 g_object_unref(smiley->loader); /* XXX: does this crash? */
5546
5547 smiley->icon = NULL;
5548 smiley->loader = NULL;
5549
5550 if (smiley->file) {
5551 /* We do not use the pixbuf loader for a smiley that can be loaded
5552 * from a file. (e.g., local custom smileys)
5553 */
5554 return;
5555 }
5556
5557 smiley->loader = gdk_pixbuf_loader_new();
5558
5559 g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(gtk_custom_smiley_allocated), smiley);
5560 g_signal_connect(smiley->loader, "closed", G_CALLBACK(gtk_custom_smiley_closed), smiley);
5561 g_signal_connect(smiley->loader, "size_prepared", G_CALLBACK(gtk_custom_smiley_size_prepared), smiley);
5562 }
5563
5564 GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide,
5565 GtkIMHtmlSmileyFlags flags)
5566 {
5567 GtkIMHtmlSmiley *smiley = g_new0(GtkIMHtmlSmiley, 1);
5568 smiley->file = g_strdup(file);
5569 smiley->smile = g_strdup(shortcut);
5570 smiley->hidden = hide;
5571 smiley->flags = flags;
5572 gtk_imhtml_smiley_reload(smiley);
5573 return smiley;
5574 }
5575
5576 void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley)
5577 {
5578 g_free(smiley->smile);
5579 g_free(smiley->file);
5580 if (smiley->icon)
5581 g_object_unref(smiley->icon);
5582 if (smiley->loader)
5583 g_object_unref(smiley->loader);
5584 g_free(smiley);
5585 }
5586