comparison pidgin/gtkimhtml.c @ 32672:3828a61c44da

A boring and large patch so I can merge heads.
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Fri, 23 Dec 2011 08:21:58 +0000
parents e2a1510da13d
children
comparison
equal deleted inserted replaced
32671:0e69949b3e61 32672:3828a61c44da
63 63
64 #include <pango/pango-font.h> 64 #include <pango/pango-font.h>
65 65
66 #define TOOLTIP_TIMEOUT 500 66 #define TOOLTIP_TIMEOUT 500
67 67
68 #if !GTK_CHECK_VERSION(2,20,0)
69 #define gtk_widget_get_realized(x) GTK_WIDGET_REALIZED(x)
70
71 #if !GTK_CHECK_VERSION(2,18,0)
72 #define gtk_widget_get_has_window(x) !GTK_WIDGET_NO_WINDOW(x)
73 #define gtk_widget_get_state(x) GTK_WIDGET_STATE(x)
74 #define gtk_widget_is_drawable(x) GTK_WIDGET_DRAWABLE(x)
75 #endif
76 #endif
77
68 static GtkTextViewClass *parent_class = NULL; 78 static GtkTextViewClass *parent_class = NULL;
69 79
70 struct scalable_data { 80 struct scalable_data {
71 GtkIMHtmlScalable *scalable; 81 GtkIMHtmlScalable *scalable;
72 GtkTextMark *mark; 82 GtkTextMark *mark;
81 struct im_image_data { 91 struct im_image_data {
82 int id; 92 int id;
83 GtkTextMark *mark; 93 GtkTextMark *mark;
84 }; 94 };
85 95
96 struct _GtkIMHtmlScalable {
97 void (*scale)(struct _GtkIMHtmlScalable *, int, int);
98 void (*add_to)(struct _GtkIMHtmlScalable *, GtkIMHtml *, GtkTextIter *);
99 void (*free)(struct _GtkIMHtmlScalable *);
100 };
101
102 struct _GtkIMHtmlHr {
103 GtkIMHtmlScalable scalable;
104 GtkWidget *sep;
105 };
106
107 struct _GtkIMHtmlImage {
108 GtkIMHtmlScalable scalable;
109 GtkImage *image; /**< Contains the scaled version of this pixbuf. */
110 GdkPixbuf *pixbuf; /**< The original pixbuf, before any scaling. */
111 GtkTextMark *mark;
112 gchar *filename;
113 int width;
114 int height;
115 int id;
116 GtkWidget *filesel;
117 };
118
119 struct _GtkIMHtmlAnimation {
120 GtkIMHtmlImage imhtmlimage;
121 GdkPixbufAnimation *anim; /**< The original animation, before any scaling. */
122 GdkPixbufAnimationIter *iter;
123 guint timer;
124 };
125
86 struct _GtkIMHtmlLink 126 struct _GtkIMHtmlLink
87 { 127 {
88 GtkIMHtml *imhtml; 128 GtkIMHtml *imhtml;
89 gchar *url; 129 gchar *url;
90 GtkTextTag *tag; 130 GtkTextTag *tag;
91 }; 131 };
92 132
93 typedef struct _GtkIMHtmlProtocol 133 struct _GtkSmileyTree {
94 { 134 GString *values;
135 GtkSmileyTree **children;
136 GtkIMHtmlSmiley *image;
137 };
138
139 typedef struct {
95 char *name; 140 char *name;
96 int length; 141 int length;
97 142
98 gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link); 143 gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link);
99 gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu); 144 gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu);
100 } GtkIMHtmlProtocol; 145 } GtkIMHtmlProtocol;
101 146
102 typedef struct _GtkIMHtmlFontDetail { 147 /* The five elements contained in a FONT tag */
148 typedef struct {
103 gushort size; 149 gushort size;
104 gchar *face; 150 gchar *face;
105 gchar *fore; 151 gchar *fore;
106 gchar *back; 152 gchar *back;
107 gchar *bg; 153 gchar *bg;
509 555
510 static gint 556 static gint
511 gtk_imhtml_tip_paint (GtkIMHtml *imhtml) 557 gtk_imhtml_tip_paint (GtkIMHtml *imhtml)
512 { 558 {
513 PangoLayout *layout; 559 PangoLayout *layout;
514 cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(imhtml->tip_window));
515 560
516 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE); 561 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
517 562
518 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip); 563 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
519 564
520 gtk_paint_flat_box (gtk_widget_get_style(imhtml->tip_window), cr, 565 gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window,
521 GTK_STATE_NORMAL, GTK_SHADOW_OUT, imhtml->tip_window, "tooltip", 566 GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, imhtml->tip_window,
522 0, 0, -1, -1); 567 "tooltip", 0, 0, -1, -1);
523 568
524 gtk_paint_layout (gtk_widget_get_style(imhtml->tip_window), cr, 569 gtk_paint_layout (imhtml->tip_window->style, imhtml->tip_window->window, GTK_STATE_NORMAL,
525 GTK_STATE_NORMAL, TRUE, imhtml->tip_window, NULL, 4, 4, layout); 570 FALSE, NULL, imhtml->tip_window, NULL, 4, 4, layout);
526 571
527 cairo_destroy(cr);
528 g_object_unref(layout); 572 g_object_unref(layout);
529 return FALSE; 573 return FALSE;
530 } 574 }
531 575
532 static gint 576 static gint
534 { 578 {
535 GtkIMHtml *imhtml = data; 579 GtkIMHtml *imhtml = data;
536 PangoFontMetrics *font_metrics; 580 PangoFontMetrics *font_metrics;
537 PangoLayout *layout; 581 PangoLayout *layout;
538 PangoFont *font; 582 PangoFont *font;
539 GtkStyle *style = gtk_widget_get_style(imhtml->tip_window); 583
540 GtkAllocation allocation;
541 gint gap, x, y, h, w, scr_w, baseline_skip; 584 gint gap, x, y, h, w, scr_w, baseline_skip;
542 585
543 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE); 586 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
544 587
545 gtk_widget_get_allocation(GTK_WIDGET(imhtml), &allocation); 588 if (!imhtml->tip || !gtk_widget_is_drawable (GTK_WIDGET(imhtml))) {
546
547 if (!imhtml->tip || !gtk_widget_is_drawable(GTK_WIDGET(imhtml))) {
548 imhtml->tip_timer = 0; 589 imhtml->tip_timer = 0;
549 return FALSE; 590 return FALSE;
550 } 591 }
551 592
552 if (imhtml->tip_window){ 593 if (imhtml->tip_window){
566 G_CALLBACK (gtk_imhtml_tip_paint), imhtml); 607 G_CALLBACK (gtk_imhtml_tip_paint), imhtml);
567 608
568 gtk_widget_ensure_style (imhtml->tip_window); 609 gtk_widget_ensure_style (imhtml->tip_window);
569 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip); 610 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
570 font = pango_context_load_font(pango_layout_get_context(layout), 611 font = pango_context_load_font(pango_layout_get_context(layout),
571 style->font_desc); 612 imhtml->tip_window->style->font_desc);
572 613
573 if (font == NULL) { 614 if (font == NULL) {
574 char *tmp = pango_font_description_to_string(style->font_desc); 615 char *tmp = pango_font_description_to_string(
616 imhtml->tip_window->style->font_desc);
575 617
576 purple_debug(PURPLE_DEBUG_ERROR, "gtk_imhtml_tip", 618 purple_debug(PURPLE_DEBUG_ERROR, "gtk_imhtml_tip",
577 "pango_context_load_font() couldn't load font: '%s'\n", 619 "pango_context_load_font() couldn't load font: '%s'\n",
578 tmp); 620 tmp);
579 g_free(tmp); 621 g_free(tmp);
594 pango_font_metrics_get_descent(font_metrics)); 636 pango_font_metrics_get_descent(font_metrics));
595 w = 8 + scr_w; 637 w = 8 + scr_w;
596 h = 8 + baseline_skip; 638 h = 8 + baseline_skip;
597 639
598 gdk_window_get_pointer (NULL, &x, &y, NULL); 640 gdk_window_get_pointer (NULL, &x, &y, NULL);
599 if ((!gtk_widget_get_has_window(GTK_WIDGET(imhtml)))) 641 if (!gtk_widget_get_has_window (GTK_WIDGET(imhtml)))
600 y += allocation.y; 642 y += GTK_WIDGET(imhtml)->allocation.y;
601 643
602 scr_w = gdk_screen_width(); 644 scr_w = gdk_screen_width();
603 645
604 x -= ((w >> 1) + 4); 646 x -= ((w >> 1) + 4);
605 647
635 gboolean hand = TRUE; 677 gboolean hand = TRUE;
636 GdkCursor *cursor = NULL; 678 GdkCursor *cursor = NULL;
637 679
638 oldprelit_tag = GTK_IMHTML(imhtml)->prelit_tag; 680 oldprelit_tag = GTK_IMHTML(imhtml)->prelit_tag;
639 681
640 gdk_window_get_pointer(gtk_widget_get_window(GTK_WIDGET(imhtml)), NULL, NULL, NULL); 682 gdk_window_get_pointer(GTK_WIDGET(imhtml)->window, NULL, NULL, NULL);
641 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml), GTK_TEXT_WINDOW_WIDGET, 683 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml), GTK_TEXT_WINDOW_WIDGET,
642 event->x, event->y, &x, &y); 684 event->x, event->y, &x, &y);
643 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y); 685 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y);
644 tags = gtk_text_iter_get_tags(&iter); 686 tags = gtk_text_iter_get_tags(&iter);
645 687
763 805
764 /* propagate the event normally */ 806 /* propagate the event normally */
765 return FALSE; 807 return FALSE;
766 } 808 }
767 809
768 /* TODO: I think this can be removed for GTK+ 3.0... */
769 #if 0
770 static gint 810 static gint
771 gtk_imhtml_expose_event (GtkWidget *widget, 811 gtk_imhtml_expose_event (GtkWidget *widget,
772 GdkEventExpose *event) 812 GdkEventExpose *event)
773 { 813 {
774 GtkTextIter start, end, cur; 814 GtkTextIter start, end, cur;
792 832
793 if (GTK_IMHTML(widget)->edit.background) { 833 if (GTK_IMHTML(widget)->edit.background) {
794 gdk_color_parse(GTK_IMHTML(widget)->edit.background, &gcolor); 834 gdk_color_parse(GTK_IMHTML(widget)->edit.background, &gcolor);
795 gdk_cairo_set_source_color(cr, &gcolor); 835 gdk_cairo_set_source_color(cr, &gcolor);
796 } else { 836 } else {
797 gdk_cairo_set_source_color(cr, 837 gdk_cairo_set_source_color(cr, &(widget->style->base[gtk_widget_get_state(widget)]));
798 &(gtk_widget_get_style(widget)->base[gtk_widget_get_state(widget)]));
799 } 838 }
800 839
801 cairo_rectangle(cr, 840 cairo_rectangle(cr,
802 visible_rect.x, visible_rect.y, 841 visible_rect.x, visible_rect.y,
803 visible_rect.width, visible_rect.height); 842 visible_rect.width, visible_rect.height);
899 return (* GTK_WIDGET_CLASS (parent_class)->expose_event) 938 return (* GTK_WIDGET_CLASS (parent_class)->expose_event)
900 (widget, event); 939 (widget, event);
901 940
902 return FALSE; 941 return FALSE;
903 } 942 }
904 #endif
905 943
906 944
907 static void paste_unformatted_cb(GtkMenuItem *menu, GtkIMHtml *imhtml) 945 static void paste_unformatted_cb(GtkMenuItem *menu, GtkIMHtml *imhtml)
908 { 946 {
909 GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD); 947 GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
1071 text = text_clipboard; 1109 text = text_clipboard;
1072 gtk_selection_data_set_text(selection_data, text, strlen(text)); 1110 gtk_selection_data_set_text(selection_data, text, strlen(text));
1073 } 1111 }
1074 if (primary) /* This was allocated here */ 1112 if (primary) /* This was allocated here */
1075 g_free(text); 1113 g_free(text);
1076 } 1114 }
1077 1115
1078 static void gtk_imhtml_primary_clipboard_clear(GtkClipboard *clipboard, GtkIMHtml *imhtml) 1116 static void gtk_imhtml_primary_clipboard_clear(GtkClipboard *clipboard, GtkIMHtml *imhtml)
1079 { 1117 {
1080 GtkTextIter insert; 1118 GtkTextIter insert;
1081 GtkTextIter selection_bound; 1119 GtkTextIter selection_bound;
1176 1214
1177 static void paste_received_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data) 1215 static void paste_received_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data)
1178 { 1216 {
1179 char *text; 1217 char *text;
1180 GtkIMHtml *imhtml = data; 1218 GtkIMHtml *imhtml = data;
1181 gint length = gtk_selection_data_get_length(selection_data);
1182 1219
1183 if (!gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml))) 1220 if (!gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml)))
1184 return; 1221 return;
1185 1222
1186 if (imhtml->wbfo || length <= 0) { 1223 if (imhtml->wbfo || selection_data->length <= 0) {
1187 gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml); 1224 gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml);
1188 return; 1225 return;
1189 } else { 1226 } else {
1190 #if 0 1227 #if 0
1191 /* Here's some debug code, for figuring out what sent to us over the clipboard. */ 1228 /* Here's some debug code, for figuring out what sent to us over the clipboard. */
1205 } 1242 }
1206 printf("\n"); 1243 printf("\n");
1207 } 1244 }
1208 #endif 1245 #endif
1209 1246
1210 text = g_malloc(length + 1); 1247 text = g_malloc(selection_data->length + 1);
1211 memcpy(text, gtk_selection_data_get_data(selection_data), length); 1248 memcpy(text, selection_data->data, selection_data->length);
1212 /* Make sure the paste data is null-terminated. Given that 1249 /* Make sure the paste data is null-terminated. Given that
1213 * we're passed length (but assume later that it is 1250 * we're passed length (but assume later that it is
1214 * null-terminated), this seems sensible to me. 1251 * null-terminated), this seems sensible to me.
1215 */ 1252 */
1216 text[length] = '\0'; 1253 text[selection_data->length] = '\0';
1217 } 1254 }
1218 1255
1219 #ifdef _WIN32 1256 #ifdef _WIN32
1220 if (gtk_selection_data_get_data_type(selection_data) == gdk_atom_intern("HTML Format", FALSE)) { 1257 if (gtk_selection_data_get_data_type(selection_data) == gdk_atom_intern("HTML Format", FALSE)) {
1221 char *tmp = clipboard_win32_to_html(text); 1258 char *tmp = clipboard_win32_to_html(text);
1222 g_free(text); 1259 g_free(text);
1223 text = tmp; 1260 text = tmp;
1224 } 1261 }
1225 #endif 1262 #endif
1226 1263
1227 if (length >= 2 && 1264 if (selection_data->length >= 2 &&
1228 (*(guint16 *)text == 0xfeff || *(guint16 *)text == 0xfffe)) { 1265 (*(guint16 *)text == 0xfeff || *(guint16 *)text == 0xfffe)) {
1229 /* This is UTF-16 */ 1266 /* This is UTF-16 */
1230 char *utf8 = utf16_to_utf8_with_bom_check(text, length); 1267 char *utf8 = utf16_to_utf8_with_bom_check(text, selection_data->length);
1231 g_free(text); 1268 g_free(text);
1232 text = utf8; 1269 text = utf8;
1233 if (!text) { 1270 if (!text) {
1234 purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in paste_received_cb\n"); 1271 purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in paste_received_cb\n");
1235 return; 1272 return;
1340 1377
1341 gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE), 1378 gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE),
1342 paste_received_cb, imhtml); 1379 paste_received_cb, imhtml);
1343 1380
1344 return TRUE; 1381 return TRUE;
1345 } 1382 }
1346 1383
1347 return FALSE; 1384 return FALSE;
1348 } 1385 }
1349 1386
1350 static void 1387 static void
1586 klass->undo = gtk_imhtml_undo; 1623 klass->undo = gtk_imhtml_undo;
1587 klass->redo = gtk_imhtml_redo; 1624 klass->redo = gtk_imhtml_redo;
1588 1625
1589 gobject_class->finalize = gtk_imhtml_finalize; 1626 gobject_class->finalize = gtk_imhtml_finalize;
1590 widget_class->drag_motion = gtk_text_view_drag_motion; 1627 widget_class->drag_motion = gtk_text_view_drag_motion;
1591 /* TODO: I _think_ this should be removed for GTK+ 3.0 */ 1628 widget_class->expose_event = gtk_imhtml_expose_event;
1592 /*widget_class->expose_event = gtk_imhtml_expose_event;*/
1593 parent_size_allocate = widget_class->size_allocate; 1629 parent_size_allocate = widget_class->size_allocate;
1594 widget_class->size_allocate = gtk_imhtml_size_allocate; 1630 widget_class->size_allocate = gtk_imhtml_size_allocate;
1595 parent_style_set = widget_class->style_set; 1631 parent_style_set = widget_class->style_set;
1596 widget_class->style_set = gtk_imhtml_style_set; 1632 widget_class->style_set = gtk_imhtml_style_set;
1597 1633
1649 _("Enable typing notification"), 1685 _("Enable typing notification"),
1650 _("Enable typing notification"), 1686 _("Enable typing notification"),
1651 TRUE, G_PARAM_READABLE)); 1687 TRUE, G_PARAM_READABLE));
1652 1688
1653 binding_set = gtk_binding_set_by_class (parent_class); 1689 binding_set = gtk_binding_set_by_class (parent_class);
1654 gtk_binding_entry_add_signal (binding_set, GDK_KEY_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD); 1690 gtk_binding_entry_add_signal (binding_set, GDK_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD);
1655 gtk_binding_entry_add_signal (binding_set, GDK_KEY_i, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_ITALIC); 1691 gtk_binding_entry_add_signal (binding_set, GDK_i, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_ITALIC);
1656 gtk_binding_entry_add_signal (binding_set, GDK_KEY_u, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_UNDERLINE); 1692 gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_UNDERLINE);
1657 gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_GROW); 1693 gtk_binding_entry_add_signal (binding_set, GDK_plus, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_GROW);
1658 gtk_binding_entry_add_signal (binding_set, GDK_KEY_equal, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_GROW); 1694 gtk_binding_entry_add_signal (binding_set, GDK_equal, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_GROW);
1659 gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_SHRINK); 1695 gtk_binding_entry_add_signal (binding_set, GDK_minus, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_SHRINK);
1660 binding_set = gtk_binding_set_by_class(klass); 1696 binding_set = gtk_binding_set_by_class(klass);
1661 gtk_binding_entry_add_signal (binding_set, GDK_KEY_r, GDK_CONTROL_MASK, "format_function_clear", 0); 1697 gtk_binding_entry_add_signal (binding_set, GDK_r, GDK_CONTROL_MASK, "format_function_clear", 0);
1662 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, "message_send", 0); 1698 gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "message_send", 0);
1663 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, "message_send", 0); 1699 gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "message_send", 0);
1664 gtk_binding_entry_add_signal (binding_set, GDK_KEY_z, GDK_CONTROL_MASK, "undo", 0); 1700 gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0);
1665 gtk_binding_entry_add_signal (binding_set, GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0); 1701 gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
1666 gtk_binding_entry_add_signal (binding_set, GDK_KEY_F14, 0, "undo", 0); 1702 gtk_binding_entry_add_signal (binding_set, GDK_F14, 0, "undo", 0);
1667 gtk_binding_entry_add_signal(binding_set, GDK_KEY_v, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "paste", 1, G_TYPE_STRING, "text"); 1703 gtk_binding_entry_add_signal(binding_set, GDK_v, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "paste", 1, G_TYPE_STRING, "text");
1668 } 1704 }
1669 1705
1670 static void gtk_imhtml_init (GtkIMHtml *imhtml) 1706 static void gtk_imhtml_init (GtkIMHtml *imhtml)
1671 { 1707 {
1672 imhtml->text_buffer = gtk_text_buffer_new(NULL); 1708 imhtml->text_buffer = gtk_text_buffer_new(NULL);
1889 1925
1890 if (gtk_drag_dest_find_target (widget, context, NULL) == GDK_NONE) { 1926 if (gtk_drag_dest_find_target (widget, context, NULL) == GDK_NONE) {
1891 /* can't accept any of the offered targets */ 1927 /* can't accept any of the offered targets */
1892 } else { 1928 } else {
1893 GtkWidget *source_widget; 1929 GtkWidget *source_widget;
1894 suggested_action = gdk_drag_context_get_suggested_action(context); 1930 suggested_action = context->suggested_action;
1895 source_widget = gtk_drag_get_source_widget (context); 1931 source_widget = gtk_drag_get_source_widget (context);
1896 if (source_widget == widget) { 1932 if (source_widget == widget) {
1897 /* Default to MOVE, unless the user has 1933 /* Default to MOVE, unless the user has
1898 * pressed ctrl or alt to affect available actions 1934 * pressed ctrl or alt to affect available actions
1899 */ 1935 */
1900 if ((gdk_drag_context_get_actions(context) & GDK_ACTION_MOVE) != 0) 1936 if ((context->actions & GDK_ACTION_MOVE) != 0)
1901 suggested_action = GDK_ACTION_MOVE; 1937 suggested_action = GDK_ACTION_MOVE;
1902 } 1938 }
1903 } 1939 }
1904 1940
1905 gdk_drag_status (context, suggested_action, time); 1941 gdk_drag_status (context, suggested_action, time);
1906 1942
1907 /* TRUE return means don't propagate the drag motion to parent 1943 /* TRUE return means don't propagate the drag motion to parent
1908 * widgets that may also be drop sites. 1944 * widgets that may also be drop sites.
1909 */ 1945 */
1910 return TRUE; 1946 return TRUE;
1911 } 1947 }
1912 1948
1913 static void 1949 static void
1914 gtk_imhtml_link_drop_cb(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data) 1950 gtk_imhtml_link_drop_cb(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data)
1915 { 1951 {
1927 gtk_imhtml_link_drag_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, 1963 gtk_imhtml_link_drag_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
1928 GtkSelectionData *sd, guint info, guint t, GtkIMHtml *imhtml) 1964 GtkSelectionData *sd, guint info, guint t, GtkIMHtml *imhtml)
1929 { 1965 {
1930 gchar **links; 1966 gchar **links;
1931 gchar *link; 1967 gchar *link;
1932 char *text = (char *) gtk_selection_data_get_data(sd); 1968 char *text = (char *)sd->data;
1933 GtkTextMark *mark = gtk_text_buffer_get_insert(imhtml->text_buffer); 1969 GtkTextMark *mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
1934 GtkTextIter iter; 1970 GtkTextIter iter;
1935 gint i = 0; 1971 gint i = 0;
1936 gint length = gtk_selection_data_get_length(sd);
1937 1972
1938 gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark); 1973 gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
1939 1974
1940 if (gtk_imhtml_get_editable(imhtml) && text) { 1975 if(gtk_imhtml_get_editable(imhtml) && sd->data){
1941 switch (info) { 1976 switch (info) {
1942 case GTK_IMHTML_DRAG_URL: 1977 case GTK_IMHTML_DRAG_URL:
1943 /* TODO: Is it really ok to change sd->data...? */ 1978 /* TODO: Is it really ok to change sd->data...? */
1944 purple_str_strip_char(text, '\r'); 1979 purple_str_strip_char((char *)sd->data, '\r');
1945 1980
1946 links = g_strsplit(text, "\n", 0); 1981 links = g_strsplit((char *)sd->data, "\n", 0);
1947 while ((link = links[i]) != NULL) { 1982 while((link = links[i]) != NULL){
1948 if (gtk_imhtml_is_protocol(link)) { 1983 if (gtk_imhtml_is_protocol(link)) {
1949 gchar *label; 1984 gchar *label;
1950 1985
1951 if(links[i + 1]) 1986 if(links[i + 1])
1952 i++; 1987 i++;
1964 return; 1999 return;
1965 } 2000 }
1966 2001
1967 i++; 2002 i++;
1968 } 2003 }
1969 g_strfreev(links); 2004 g_strfreev(links);
1970 break; 2005 break;
1971 case GTK_IMHTML_DRAG_HTML: 2006 case GTK_IMHTML_DRAG_HTML:
1972 { 2007 {
1973 char *utf8 = NULL; 2008 char *utf8 = NULL;
1974 /* Ewww. This is all because mozilla thinks that text/html is 'for internal use only.' 2009 /* Ewww. This is all because mozilla thinks that text/html is 'for internal use only.'
1980 * the string as utf8 and if that fails we assume it is ucs2 2015 * the string as utf8 and if that fails we assume it is ucs2
1981 * 2016 *
1982 * See also the comment on text/html here: 2017 * See also the comment on text/html here:
1983 * http://mail.gnome.org/archives/gtk-devel-list/2001-September/msg00114.html 2018 * http://mail.gnome.org/archives/gtk-devel-list/2001-September/msg00114.html
1984 */ 2019 */
1985 if (length >= 2 && !g_utf8_validate(text, length - 1, NULL)) { 2020 if (sd->length >= 2 && !g_utf8_validate(text, sd->length - 1, NULL)) {
1986 utf8 = utf16_to_utf8_with_bom_check(text, length); 2021 utf8 = utf16_to_utf8_with_bom_check(text, sd->length);
1987 2022
1988 if (!utf8) { 2023 if (!utf8) {
1989 purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in drag_rcv_cb\n"); 2024 purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in drag_rcv_cb\n");
1990 return; 2025 return;
1991 } 2026 }
2010 break; 2045 break;
2011 default: 2046 default:
2012 gtk_drag_finish(dc, FALSE, FALSE, t); 2047 gtk_drag_finish(dc, FALSE, FALSE, t);
2013 return; 2048 return;
2014 } 2049 }
2015 gtk_drag_finish(dc, TRUE, 2050 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
2016 gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
2017 } else { 2051 } else {
2018 gtk_drag_finish(dc, FALSE, FALSE, t); 2052 gtk_drag_finish(dc, FALSE, FALSE, t);
2019 } 2053 }
2020 } 2054 }
2021 2055
2542 * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom. 2576 * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom.
2543 */ 2577 */
2544 static gboolean smooth_scroll_cb(gpointer data) 2578 static gboolean smooth_scroll_cb(gpointer data)
2545 { 2579 {
2546 GtkIMHtml *imhtml = data; 2580 GtkIMHtml *imhtml = data;
2547 GtkAdjustment *adj = gtk_text_view_get_vadjustment(GTK_TEXT_VIEW(imhtml)); 2581 GtkAdjustment *adj = GTK_TEXT_VIEW(imhtml)->vadjustment;
2548 gdouble max_val = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj); 2582 gdouble max_val = adj->upper - adj->page_size;
2549 gdouble scroll_val = gtk_adjustment_get_value(adj) + ((max_val - gtk_adjustment_get_value(adj)) / 3); 2583 gdouble scroll_val = gtk_adjustment_get_value(adj) + ((max_val - gtk_adjustment_get_value(adj)) / 3);
2550 2584
2551 g_return_val_if_fail(imhtml->scroll_time != NULL, FALSE); 2585 g_return_val_if_fail(imhtml->scroll_time != NULL, FALSE);
2552 2586
2553 if (g_timer_elapsed(imhtml->scroll_time, NULL) > MAX_SCROLL_TIME || scroll_val >= max_val) { 2587 if (g_timer_elapsed(imhtml->scroll_time, NULL) > MAX_SCROLL_TIME || scroll_val >= max_val) {
2566 } 2600 }
2567 2601
2568 static gboolean scroll_idle_cb(gpointer data) 2602 static gboolean scroll_idle_cb(gpointer data)
2569 { 2603 {
2570 GtkIMHtml *imhtml = data; 2604 GtkIMHtml *imhtml = data;
2571 GtkAdjustment *adj = gtk_text_view_get_vadjustment(GTK_TEXT_VIEW(imhtml)); 2605 GtkAdjustment *adj = GTK_TEXT_VIEW(imhtml)->vadjustment;
2572 if (adj) { 2606 if(adj) {
2573 gtk_adjustment_set_value(adj, gtk_adjustment_get_upper(adj) - 2607 gtk_adjustment_set_value(adj, adj->upper - adj->page_size);
2574 gtk_adjustment_get_page_size(adj));
2575 } 2608 }
2576 imhtml->scroll_src = 0; 2609 imhtml->scroll_src = 0;
2577 return FALSE; 2610 return FALSE;
2578 } 2611 }
2579 2612
3604 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x, 3637 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x,
3605 rect.y + rect.height); 3638 rect.y + rect.height);
3606 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0); 3639 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0);
3607 } 3640 }
3608 3641
3609 /* GtkIMHtmlScalable, gtk_imhtml_image, gtk_imhtml_hr */ 3642 /**
3610 GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id) 3643 * Destroys and frees a GTK+ IM/HTML scalable image.
3611 { 3644 *
3612 GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage)); 3645 * @param scale The GTK+ IM/HTML scalable.
3613 3646 */
3614 GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale; 3647 static void gtk_imhtml_image_free(GtkIMHtmlScalable *scale)
3615 GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to; 3648 {
3616 GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_image_free; 3649 GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
3617 3650
3618 im_image->pixbuf = img; 3651 g_object_unref(image->pixbuf);
3619 im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf)); 3652 g_free(image->filename);
3620 im_image->width = gdk_pixbuf_get_width(img); 3653 if (image->filesel)
3621 im_image->height = gdk_pixbuf_get_height(img); 3654 gtk_widget_destroy(image->filesel);
3622 im_image->mark = NULL; 3655 g_free(scale);
3623 im_image->filename = g_strdup(filename); 3656 }
3624 im_image->id = id; 3657
3625 im_image->filesel = NULL; 3658 /**
3626 3659 * Destroys and frees a GTK+ IM/HTML scalable animation.
3627 g_object_ref(img); 3660 *
3628 return GTK_IMHTML_SCALABLE(im_image); 3661 * @param scale The GTK+ IM/HTML scalable.
3629 } 3662 */
3630 3663 static void gtk_imhtml_animation_free(GtkIMHtmlScalable *scale)
3631 static gboolean 3664 {
3632 animate_image_cb(gpointer data) 3665 GtkIMHtmlAnimation *animation = (GtkIMHtmlAnimation *)scale;
3633 { 3666
3634 GtkIMHtmlImage *im_image; 3667 if (animation->timer > 0)
3635 int width, height; 3668 g_source_remove(animation->timer);
3636 int delay; 3669 if (animation->iter != NULL)
3637 3670 g_object_unref(animation->iter);
3638 im_image = data; 3671 g_object_unref(animation->anim);
3639 3672
3640 /* Update the pointer to this GdkPixbuf frame of the animation */ 3673 gtk_imhtml_image_free(scale);
3641 if (gdk_pixbuf_animation_iter_advance(GTK_IMHTML_ANIMATION(im_image)->iter, NULL)) {
3642 GdkPixbuf *pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
3643 g_object_unref(G_OBJECT(im_image->pixbuf));
3644 im_image->pixbuf = gdk_pixbuf_copy(pb);
3645
3646 /* Update the displayed GtkImage */
3647 width = gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image));
3648 height = gdk_pixbuf_get_height(gtk_image_get_pixbuf(im_image->image));
3649 if (width > 0 && height > 0)
3650 {
3651 /* Need to scale the new frame to the same size as the old frame */
3652 GdkPixbuf *tmp;
3653 tmp = gdk_pixbuf_scale_simple(im_image->pixbuf, width, height, GDK_INTERP_BILINEAR);
3654 gtk_image_set_from_pixbuf(im_image->image, tmp);
3655 g_object_unref(G_OBJECT(tmp));
3656 } else {
3657 /* Display at full-size */
3658 gtk_image_set_from_pixbuf(im_image->image, im_image->pixbuf);
3659 }
3660 }
3661
3662 delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
3663 GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
3664
3665 return FALSE;
3666 }
3667
3668 GtkIMHtmlScalable *gtk_imhtml_animation_new(GdkPixbufAnimation *anim, const gchar *filename, int id)
3669 {
3670 GtkIMHtmlImage *im_image = (GtkIMHtmlImage *) g_new0(GtkIMHtmlAnimation, 1);
3671
3672 GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
3673 GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
3674 GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_animation_free;
3675
3676 GTK_IMHTML_ANIMATION(im_image)->anim = anim;
3677 if (gdk_pixbuf_animation_is_static_image(anim)) {
3678 im_image->pixbuf = gdk_pixbuf_animation_get_static_image(anim);
3679 g_object_ref(im_image->pixbuf);
3680 } else {
3681 int delay;
3682 GdkPixbuf *pb;
3683 GTK_IMHTML_ANIMATION(im_image)->iter = gdk_pixbuf_animation_get_iter(anim, NULL);
3684 pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
3685 im_image->pixbuf = gdk_pixbuf_copy(pb);
3686 delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
3687 GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
3688 }
3689 im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
3690 im_image->width = gdk_pixbuf_animation_get_width(anim);
3691 im_image->height = gdk_pixbuf_animation_get_height(anim);
3692 im_image->filename = g_strdup(filename);
3693 im_image->id = id;
3694
3695 g_object_ref(anim);
3696
3697 return GTK_IMHTML_SCALABLE(im_image);
3698 }
3699
3700 void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height)
3701 {
3702 GtkIMHtmlImage *im_image = (GtkIMHtmlImage *)scale;
3703
3704 if (im_image->width > width || im_image->height > height) {
3705 double ratio_w, ratio_h, ratio;
3706 int new_h, new_w;
3707 GdkPixbuf *new_image = NULL;
3708
3709 ratio_w = ((double)width - 2) / im_image->width;
3710 ratio_h = ((double)height - 2) / im_image->height;
3711
3712 ratio = (ratio_w < ratio_h) ? ratio_w : ratio_h;
3713
3714 new_w = (int)(im_image->width * ratio);
3715 new_h = (int)(im_image->height * ratio);
3716
3717 new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, new_w, new_h, GDK_INTERP_BILINEAR);
3718 gtk_image_set_from_pixbuf(im_image->image, new_image);
3719 g_object_unref(G_OBJECT(new_image));
3720 } else if (gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image)) != im_image->width) {
3721 /* Enough space to show the full-size of the image. */
3722 GdkPixbuf *new_image;
3723
3724 new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, im_image->width, im_image->height, GDK_INTERP_BILINEAR);
3725 gtk_image_set_from_pixbuf(im_image->image, new_image);
3726 g_object_unref(G_OBJECT(new_image));
3727 }
3728 } 3674 }
3729 3675
3730 static void 3676 static void
3731 image_save_yes_cb(GtkIMHtmlImageSave *save, const char *filename) 3677 image_save_yes_cb(GtkIMHtmlImageSave *save, const char *filename)
3732 { 3678 {
3929 else 3875 else
3930 return FALSE; /* Let clicks go through if we didn't catch anything */ 3876 return FALSE; /* Let clicks go through if we didn't catch anything */
3931 3877
3932 } 3878 }
3933 3879
3934 static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley) 3880 /**
3935 { 3881 * Rescales a GTK+ IM/HTML scalable image to a given size.
3936 GdkPixbufAnimation *anim = NULL; 3882 *
3937 GtkIMHtmlImageSave *save = NULL; 3883 * @param scale The GTK+ IM/HTML scalable.
3938 gboolean ret; 3884 * @param width The new width.
3939 3885 * @param height The new height.
3940 if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3) 3886 */
3941 return FALSE; 3887 static void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height)
3942 3888 {
3943 anim = gtk_smiley_get_image(smiley); 3889 GtkIMHtmlImage *im_image = (GtkIMHtmlImage *)scale;
3944 if (!anim) 3890
3945 return FALSE; 3891 if (im_image->width > width || im_image->height > height) {
3946 3892 double ratio_w, ratio_h, ratio;
3947 save = g_new0(GtkIMHtmlImageSave, 1); 3893 int new_h, new_w;
3948 save->image = (GtkIMHtmlScalable *)gtk_imhtml_animation_new(anim, smiley->smile, 0); 3894 GdkPixbuf *new_image = NULL;
3949 save->data = smiley->data; /* Do not need to memdup here, since the smiley is not 3895
3950 destroyed before this GtkIMHtmlImageSave */ 3896 ratio_w = ((double)width - 2) / im_image->width;
3951 save->datasize = smiley->datasize; 3897 ratio_h = ((double)height - 2) / im_image->height;
3952 ret = gtk_imhtml_image_clicked(w, event, save); 3898
3953 g_object_set_data_full(G_OBJECT(w), "image-data", save->image, (GDestroyNotify)gtk_imhtml_animation_free); 3899 ratio = (ratio_w < ratio_h) ? ratio_w : ratio_h;
3954 g_object_set_data_full(G_OBJECT(w), "image-save-data", save, (GDestroyNotify)g_free); 3900
3955 return ret; 3901 new_w = (int)(im_image->width * ratio);
3956 } 3902 new_h = (int)(im_image->height * ratio);
3957 3903
3958 void gtk_imhtml_image_free(GtkIMHtmlScalable *scale) 3904 new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, new_w, new_h, GDK_INTERP_BILINEAR);
3959 { 3905 gtk_image_set_from_pixbuf(im_image->image, new_image);
3960 GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale; 3906 g_object_unref(G_OBJECT(new_image));
3961 3907 } else if (gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image)) != im_image->width) {
3962 g_object_unref(image->pixbuf); 3908 /* Enough space to show the full-size of the image. */
3963 g_free(image->filename); 3909 GdkPixbuf *new_image;
3964 if (image->filesel) 3910
3965 gtk_widget_destroy(image->filesel); 3911 new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, im_image->width, im_image->height, GDK_INTERP_BILINEAR);
3966 g_free(scale); 3912 gtk_image_set_from_pixbuf(im_image->image, new_image);
3967 } 3913 g_object_unref(G_OBJECT(new_image));
3968 3914 }
3969 void gtk_imhtml_animation_free(GtkIMHtmlScalable *scale) 3915 }
3970 { 3916
3971 GtkIMHtmlAnimation *animation = (GtkIMHtmlAnimation *)scale; 3917 /**
3972 3918 * Adds a GTK+ IM/HTML scalable image to a given GTK+ IM/HTML at a given iter.
3973 if (animation->timer > 0) 3919 *
3974 g_source_remove(animation->timer); 3920 * @param scale The GTK+ IM/HTML scalable.
3975 if (animation->iter != NULL) 3921 * @param imhtml The GTK+ IM/HTML.
3976 g_object_unref(animation->iter); 3922 * @param iter The GtkTextIter at which to add the scalable.
3977 g_object_unref(animation->anim); 3923 */
3978 3924 static void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
3979 gtk_imhtml_image_free(scale);
3980 }
3981
3982 void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
3983 { 3925 {
3984 GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale; 3926 GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
3985 GtkWidget *box = gtk_event_box_new(); 3927 GtkWidget *box = gtk_event_box_new();
3986 char *tag; 3928 char *tag;
3987 GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter); 3929 GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
4003 3945
4004 save = g_new0(GtkIMHtmlImageSave, 1); 3946 save = g_new0(GtkIMHtmlImageSave, 1);
4005 save->image = scale; 3947 save->image = scale;
4006 g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gtk_imhtml_image_clicked), save); 3948 g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gtk_imhtml_image_clicked), save);
4007 g_object_set_data_full(G_OBJECT(box), "image-save-data", save, (GDestroyNotify)g_free); 3949 g_object_set_data_full(G_OBJECT(box), "image-save-data", save, (GDestroyNotify)g_free);
3950 }
3951
3952 /**
3953 * Creates and returns a new GTK+ IM/HTML scalable object with an image.
3954 *
3955 * @param img A GdkPixbuf of the image to add.
3956 * @param filename The filename to associate with the image.
3957 * @param id The id to associate with the image.
3958 *
3959 * @return A new IM/HTML Scalable object with an image.
3960 */
3961 static GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id)
3962 {
3963 GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage));
3964
3965 GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
3966 GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
3967 GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_image_free;
3968
3969 im_image->pixbuf = img;
3970 im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
3971 im_image->width = gdk_pixbuf_get_width(img);
3972 im_image->height = gdk_pixbuf_get_height(img);
3973 im_image->mark = NULL;
3974 im_image->filename = g_strdup(filename);
3975 im_image->id = id;
3976 im_image->filesel = NULL;
3977
3978 g_object_ref(img);
3979 return GTK_IMHTML_SCALABLE(im_image);
3980 }
3981
3982 static gboolean
3983 animate_image_cb(gpointer data)
3984 {
3985 GtkIMHtmlImage *im_image;
3986 int width, height;
3987 int delay;
3988
3989 im_image = data;
3990
3991 /* Update the pointer to this GdkPixbuf frame of the animation */
3992 if (gdk_pixbuf_animation_iter_advance(GTK_IMHTML_ANIMATION(im_image)->iter, NULL)) {
3993 GdkPixbuf *pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
3994 g_object_unref(G_OBJECT(im_image->pixbuf));
3995 im_image->pixbuf = gdk_pixbuf_copy(pb);
3996
3997 /* Update the displayed GtkImage */
3998 width = gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image));
3999 height = gdk_pixbuf_get_height(gtk_image_get_pixbuf(im_image->image));
4000 if (width > 0 && height > 0)
4001 {
4002 /* Need to scale the new frame to the same size as the old frame */
4003 GdkPixbuf *tmp;
4004 tmp = gdk_pixbuf_scale_simple(im_image->pixbuf, width, height, GDK_INTERP_BILINEAR);
4005 gtk_image_set_from_pixbuf(im_image->image, tmp);
4006 g_object_unref(G_OBJECT(tmp));
4007 } else {
4008 /* Display at full-size */
4009 gtk_image_set_from_pixbuf(im_image->image, im_image->pixbuf);
4010 }
4011 }
4012
4013 delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
4014 GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
4015
4016 return FALSE;
4017 }
4018
4019 /**
4020 * Creates and returns a new GTK+ IM/HTML scalable object with an
4021 * animated image.
4022 *
4023 * @param img A GdkPixbufAnimation of the image to add.
4024 * @param filename The filename to associate with the image.
4025 * @param id The id to associate with the image.
4026 *
4027 * @return A new IM/HTML Scalable object with an image.
4028 */
4029 /*
4030 * TODO: All this animation code could be combined much better with
4031 * the image code. It couldn't be done when it was written
4032 * because it requires breaking backward compatibility. It
4033 * would be good to do it for 3.0.0.
4034 */
4035 static GtkIMHtmlScalable *gtk_imhtml_animation_new(GdkPixbufAnimation *anim, const gchar *filename, int id)
4036 {
4037 GtkIMHtmlImage *im_image = (GtkIMHtmlImage *) g_new0(GtkIMHtmlAnimation, 1);
4038
4039 GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
4040 GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
4041 GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_animation_free;
4042
4043 GTK_IMHTML_ANIMATION(im_image)->anim = anim;
4044 if (gdk_pixbuf_animation_is_static_image(anim)) {
4045 im_image->pixbuf = gdk_pixbuf_animation_get_static_image(anim);
4046 g_object_ref(im_image->pixbuf);
4047 } else {
4048 int delay;
4049 GdkPixbuf *pb;
4050 GTK_IMHTML_ANIMATION(im_image)->iter = gdk_pixbuf_animation_get_iter(anim, NULL);
4051 pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
4052 im_image->pixbuf = gdk_pixbuf_copy(pb);
4053 delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
4054 GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
4055 }
4056 im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
4057 im_image->width = gdk_pixbuf_animation_get_width(anim);
4058 im_image->height = gdk_pixbuf_animation_get_height(anim);
4059 im_image->filename = g_strdup(filename);
4060 im_image->id = id;
4061
4062 g_object_ref(anim);
4063
4064 return GTK_IMHTML_SCALABLE(im_image);
4008 } 4065 }
4009 4066
4010 GtkIMHtmlScalable *gtk_imhtml_hr_new() 4067 GtkIMHtmlScalable *gtk_imhtml_hr_new()
4011 { 4068 {
4012 GtkIMHtmlHr *hr = g_malloc(sizeof(GtkIMHtmlHr)); 4069 GtkIMHtmlHr *hr = g_malloc(sizeof(GtkIMHtmlHr));
4227 4284
4228 tags = gtk_text_iter_get_tags(i); 4285 tags = gtk_text_iter_get_tags(i);
4229 4286
4230 for (l = tags; l; l = l->next) { 4287 for (l = tags; l; l = l->next) {
4231 GtkTextTag *tag = l->data; 4288 GtkTextTag *tag = l->data;
4232 gchar *name = NULL; 4289
4233 4290 if (tag->name && !strncmp(tag->name, prefix, len))
4234 g_object_get(G_OBJECT(tag), "name", &name, NULL);
4235
4236 if (name && !strncmp(name, prefix, len))
4237 gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, i, e); 4291 gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, i, e);
4238
4239 g_free(name);
4240 } 4292 }
4241 4293
4242 g_slist_free(tags); 4294 g_slist_free(tags);
4243 4295
4244 if (homo) 4296 if (homo)
4250 if (gtk_text_iter_begins_tag(&iter, NULL)) { 4302 if (gtk_text_iter_begins_tag(&iter, NULL)) {
4251 tags = gtk_text_iter_get_toggled_tags(&iter, TRUE); 4303 tags = gtk_text_iter_get_toggled_tags(&iter, TRUE);
4252 4304
4253 for (l = tags; l; l = l->next) { 4305 for (l = tags; l; l = l->next) {
4254 GtkTextTag *tag = l->data; 4306 GtkTextTag *tag = l->data;
4255 gchar *name = NULL; 4307
4256 4308 if (tag->name && !strncmp(tag->name, prefix, len))
4257 g_object_get(G_OBJECT(tag), "name", &name, NULL);
4258
4259 if (name && !strncmp(name, prefix, len))
4260 gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, &iter, e); 4309 gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, &iter, e);
4261
4262 g_free(name);
4263 } 4310 }
4264 4311
4265 g_slist_free(tags); 4312 g_slist_free(tags);
4266 } 4313 }
4267 } 4314 }
4378 if (tag && /* Remove the formatting only if */ 4425 if (tag && /* Remove the formatting only if */
4379 gtk_text_iter_starts_word(start) && /* beginning of a word */ 4426 gtk_text_iter_starts_word(start) && /* beginning of a word */
4380 gtk_text_iter_begins_tag(start, tag) && /* the tag starts with the selection */ 4427 gtk_text_iter_begins_tag(start, tag) && /* the tag starts with the selection */
4381 (!gtk_text_iter_has_tag(end, tag) || /* the tag ends within the selection */ 4428 (!gtk_text_iter_has_tag(end, tag) || /* the tag ends within the selection */
4382 gtk_text_iter_ends_tag(end, tag))) { 4429 gtk_text_iter_ends_tag(end, tag))) {
4383 gchar *name = NULL;
4384
4385 g_object_get(G_OBJECT(tag), "name", &name, NULL);
4386 gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, start, end); 4430 gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, start, end);
4387 4431 if (tag->name &&
4388 if (name && strncmp(name, "LINK ", 5) == 0 && imhtml->edit.link) { 4432 strncmp(tag->name, "LINK ", 5) == 0 && imhtml->edit.link) {
4389 gtk_imhtml_toggle_link(imhtml, NULL); 4433 gtk_imhtml_toggle_link(imhtml, NULL);
4390 } 4434 }
4391
4392 g_free(name);
4393 } 4435 }
4394 } 4436 }
4395 g_slist_free(tags); 4437 g_slist_free(tags);
4396 } 4438 }
4397 4439
4609 else 4651 else
4610 tags = gtk_text_iter_get_tags(&iter); 4652 tags = gtk_text_iter_get_tags(&iter);
4611 4653
4612 for (l = tags; l != NULL; l = l->next) { 4654 for (l = tags; l != NULL; l = l->next) {
4613 GtkTextTag *tag = GTK_TEXT_TAG(l->data); 4655 GtkTextTag *tag = GTK_TEXT_TAG(l->data);
4614 gchar *name = NULL; 4656
4615 4657 if (tag->name) {
4616 g_object_get(G_OBJECT(tag), "name", &name, NULL); 4658 if (strcmp(tag->name, "BOLD") == 0)
4617
4618 if (name) {
4619 if (strcmp(name, "BOLD") == 0)
4620 imhtml->edit.bold = TRUE; 4659 imhtml->edit.bold = TRUE;
4621 else if (strcmp(name, "ITALICS") == 0) 4660 else if (strcmp(tag->name, "ITALICS") == 0)
4622 imhtml->edit.italic = TRUE; 4661 imhtml->edit.italic = TRUE;
4623 else if (strcmp(name, "UNDERLINE") == 0) 4662 else if (strcmp(tag->name, "UNDERLINE") == 0)
4624 imhtml->edit.underline = TRUE; 4663 imhtml->edit.underline = TRUE;
4625 else if (strcmp(name, "STRIKE") == 0) 4664 else if (strcmp(tag->name, "STRIKE") == 0)
4626 imhtml->edit.strike = TRUE; 4665 imhtml->edit.strike = TRUE;
4627 else if (strncmp(name, "FORECOLOR ", 10) == 0) 4666 else if (strncmp(tag->name, "FORECOLOR ", 10) == 0)
4628 imhtml->edit.forecolor = g_strdup(&(name)[10]); 4667 imhtml->edit.forecolor = g_strdup(&(tag->name)[10]);
4629 else if (strncmp(name, "BACKCOLOR ", 10) == 0) 4668 else if (strncmp(tag->name, "BACKCOLOR ", 10) == 0)
4630 imhtml->edit.backcolor = g_strdup(&(name)[10]); 4669 imhtml->edit.backcolor = g_strdup(&(tag->name)[10]);
4631 else if (strncmp(name, "FONT FACE ", 10) == 0) 4670 else if (strncmp(tag->name, "FONT FACE ", 10) == 0)
4632 imhtml->edit.fontface = g_strdup(&(name)[10]); 4671 imhtml->edit.fontface = g_strdup(&(tag->name)[10]);
4633 else if (strncmp(name, "FONT SIZE ", 10) == 0) 4672 else if (strncmp(tag->name, "FONT SIZE ", 10) == 0)
4634 imhtml->edit.fontsize = strtol(&(name)[10], NULL, 10); 4673 imhtml->edit.fontsize = strtol(&(tag->name)[10], NULL, 10);
4635 else if ((strncmp(name, "LINK ", 5) == 0) && !gtk_text_iter_is_end(&iter)) 4674 else if ((strncmp(tag->name, "LINK ", 5) == 0) && !gtk_text_iter_is_end(&iter))
4636 imhtml->edit.link = tag; 4675 imhtml->edit.link = tag;
4637 } 4676 }
4638
4639 g_free(name);
4640 } 4677 }
4641 4678
4642 g_slist_free(tags); 4679 g_slist_free(tags);
4643 } 4680 }
4644 4681
4925 gtk_text_buffer_begin_user_action(imhtml->text_buffer); 4962 gtk_text_buffer_begin_user_action(imhtml->text_buffer);
4926 gtk_imhtml_insert_smiley_at_iter(imhtml, sml, smiley, &iter); 4963 gtk_imhtml_insert_smiley_at_iter(imhtml, sml, smiley, &iter);
4927 gtk_text_buffer_end_user_action(imhtml->text_buffer); 4964 gtk_text_buffer_end_user_action(imhtml->text_buffer);
4928 } 4965 }
4929 4966
4930 /* TODO: I think this can be removed for GTK+ 3.0... */
4931 #if 0
4932 static gboolean 4967 static gboolean
4933 image_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) 4968 image_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
4934 { 4969 {
4935 GTK_WIDGET_CLASS(GTK_WIDGET_GET_CLASS(widget))->expose_event(widget, event); 4970 GTK_WIDGET_CLASS(GTK_WIDGET_GET_CLASS(widget))->expose_event(widget, event);
4936 4971
4937 return TRUE; 4972 return TRUE;
4938 } 4973 }
4939 #endif
4940 4974
4941 /* In case the smiley gets removed from the imhtml before it gets removed from the queue */ 4975 /* In case the smiley gets removed from the imhtml before it gets removed from the queue */
4942 static void animated_smiley_destroy_cb(GtkWidget *widget, GtkIMHtml *imhtml) 4976 static void animated_smiley_destroy_cb(GtkObject *widget, GtkIMHtml *imhtml)
4943 { 4977 {
4944 GList *l = imhtml->animations->head; 4978 GList *l = imhtml->animations->head;
4945 while (l) { 4979 while (l) {
4946 GList *next = l->next; 4980 GList *next = l->next;
4947 if (l->data == widget) { 4981 if (l->data == widget) {
4950 imhtml->animations->head = g_list_delete_link(imhtml->animations->head, l); 4984 imhtml->animations->head = g_list_delete_link(imhtml->animations->head, l);
4951 imhtml->num_animations--; 4985 imhtml->num_animations--;
4952 } 4986 }
4953 l = next; 4987 l = next;
4954 } 4988 }
4989 }
4990
4991 static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley)
4992 {
4993 GdkPixbufAnimation *anim = NULL;
4994 GtkIMHtmlImageSave *save = NULL;
4995 gboolean ret;
4996
4997 if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3)
4998 return FALSE;
4999
5000 anim = gtk_smiley_get_image(smiley);
5001 if (!anim)
5002 return FALSE;
5003
5004 save = g_new0(GtkIMHtmlImageSave, 1);
5005 save->image = (GtkIMHtmlScalable *)gtk_imhtml_animation_new(anim, smiley->smile, 0);
5006 save->data = smiley->data; /* Do not need to memdup here, since the smiley is not
5007 destroyed before this GtkIMHtmlImageSave */
5008 save->datasize = smiley->datasize;
5009 ret = gtk_imhtml_image_clicked(w, event, save);
5010 g_object_set_data_full(G_OBJECT(w), "image-data", save->image, (GDestroyNotify)gtk_imhtml_animation_free);
5011 g_object_set_data_full(G_OBJECT(w), "image-save-data", save, (GDestroyNotify)g_free);
5012 return ret;
4955 } 5013 }
4956 5014
4957 void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *smiley, GtkTextIter *iter) 5015 void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *smiley, GtkTextIter *iter)
4958 { 5016 {
4959 GdkPixbuf *pixbuf = NULL; 5017 GdkPixbuf *pixbuf = NULL;
5008 gtk_image_set_from_pixbuf(image, copy); 5066 gtk_image_set_from_pixbuf(image, copy);
5009 g_object_unref(G_OBJECT(copy)); 5067 g_object_unref(G_OBJECT(copy));
5010 } 5068 }
5011 } 5069 }
5012 } else { 5070 } else {
5013 imhtml->num_animations++; 5071 imhtml->num_animations++;
5014 } 5072 }
5015 g_signal_connect(G_OBJECT(icon), "destroy", G_CALLBACK(animated_smiley_destroy_cb), imhtml); 5073 g_signal_connect(G_OBJECT(icon), "destroy", G_CALLBACK(animated_smiley_destroy_cb), imhtml);
5016 g_queue_push_tail(imhtml->animations, icon); 5074 g_queue_push_tail(imhtml->animations, icon);
5017 } 5075 }
5018 } 5076 }
5032 5090
5033 /* This catches the expose events generated by animated 5091 /* This catches the expose events generated by animated
5034 * images, and ensures that they are handled by the image 5092 * images, and ensures that they are handled by the image
5035 * itself, without propagating to the textview and causing 5093 * itself, without propagating to the textview and causing
5036 * a complete refresh */ 5094 * a complete refresh */
5037 /* TODO: I think this should be removed for GTK+ 3.0?
5038 g_signal_connect(G_OBJECT(icon), "expose-event", G_CALLBACK(image_expose), NULL); 5095 g_signal_connect(G_OBJECT(icon), "expose-event", G_CALLBACK(image_expose), NULL);
5039 */
5040 5096
5041 gtk_widget_show(icon); 5097 gtk_widget_show(icon);
5042 if (ebox) 5098 if (ebox)
5043 gtk_container_add(GTK_CONTAINER(ebox), icon); 5099 gtk_container_add(GTK_CONTAINER(ebox), icon);
5044 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox ? ebox : icon, anchor); 5100 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox ? ebox : icon, anchor);
5128 imhtml->scalables = g_list_append(imhtml->scalables, sd); 5184 imhtml->scalables = g_list_append(imhtml->scalables, sd);
5129 } 5185 }
5130 5186
5131 static const gchar *tag_to_html_start(GtkTextTag *tag) 5187 static const gchar *tag_to_html_start(GtkTextTag *tag)
5132 { 5188 {
5189 const gchar *name;
5133 static gchar buf[1024]; 5190 static gchar buf[1024];
5134 gchar *name = NULL; 5191
5135 5192 name = tag->name;
5136 g_return_val_if_fail(name != NULL, ""); 5193 g_return_val_if_fail(name != NULL, "");
5137 g_object_get(G_OBJECT(tag), "name", &name, NULL);
5138 5194
5139 if (strcmp(name, "BOLD") == 0) { 5195 if (strcmp(name, "BOLD") == 0) {
5140 g_free(name);
5141 return "<b>"; 5196 return "<b>";
5142 } else if (strcmp(name, "ITALICS") == 0) { 5197 } else if (strcmp(name, "ITALICS") == 0) {
5143 g_free(name);
5144 return "<i>"; 5198 return "<i>";
5145 } else if (strcmp(name, "UNDERLINE") == 0) { 5199 } else if (strcmp(name, "UNDERLINE") == 0) {
5146 g_free(name);
5147 return "<u>"; 5200 return "<u>";
5148 } else if (strcmp(name, "STRIKE") == 0) { 5201 } else if (strcmp(name, "STRIKE") == 0) {
5149 g_free(name);
5150 return "<s>"; 5202 return "<s>";
5151 } else if (strncmp(name, "LINK ", 5) == 0) { 5203 } else if (strncmp(name, "LINK ", 5) == 0) {
5152 char *tmp = g_object_get_data(G_OBJECT(tag), "link_url"); 5204 char *tmp = g_object_get_data(G_OBJECT(tag), "link_url");
5153
5154 g_free(name);
5155
5156 if (tmp) { 5205 if (tmp) {
5157 g_snprintf(buf, sizeof(buf), "<a href=\"%s\">", tmp); 5206 g_snprintf(buf, sizeof(buf), "<a href=\"%s\">", tmp);
5158 buf[sizeof(buf)-1] = '\0'; 5207 buf[sizeof(buf)-1] = '\0';
5159 return buf; 5208 return buf;
5160 } else { 5209 } else {
5161 return ""; 5210 return "";
5162 } 5211 }
5163 } else if (strncmp(name, "FORECOLOR ", 10) == 0) { 5212 } else if (strncmp(name, "FORECOLOR ", 10) == 0) {
5164 g_snprintf(buf, sizeof(buf), "<font color=\"%s\">", &name[10]); 5213 g_snprintf(buf, sizeof(buf), "<font color=\"%s\">", &name[10]);
5165
5166 g_free(name);
5167
5168 return buf; 5214 return buf;
5169 } else if (strncmp(name, "BACKCOLOR ", 10) == 0) { 5215 } else if (strncmp(name, "BACKCOLOR ", 10) == 0) {
5170 g_snprintf(buf, sizeof(buf), "<font back=\"%s\">", &name[10]); 5216 g_snprintf(buf, sizeof(buf), "<font back=\"%s\">", &name[10]);
5171 g_free(name);
5172
5173 return buf; 5217 return buf;
5174 } else if (strncmp(name, "BACKGROUND ", 10) == 0) { 5218 } else if (strncmp(name, "BACKGROUND ", 10) == 0) {
5175 g_snprintf(buf, sizeof(buf), "<body bgcolor=\"%s\">", &name[11]); 5219 g_snprintf(buf, sizeof(buf), "<body bgcolor=\"%s\">", &name[11]);
5176 g_free(name);
5177
5178 return buf; 5220 return buf;
5179 } else if (strncmp(name, "FONT FACE ", 10) == 0) { 5221 } else if (strncmp(name, "FONT FACE ", 10) == 0) {
5180 g_snprintf(buf, sizeof(buf), "<font face=\"%s\">", &name[10]); 5222 g_snprintf(buf, sizeof(buf), "<font face=\"%s\">", &name[10]);
5181 g_free(name);
5182
5183 return buf; 5223 return buf;
5184 } else if (strncmp(name, "FONT SIZE ", 10) == 0) { 5224 } else if (strncmp(name, "FONT SIZE ", 10) == 0) {
5185 g_snprintf(buf, sizeof(buf), "<font size=\"%s\">", &name[10]); 5225 g_snprintf(buf, sizeof(buf), "<font size=\"%s\">", &name[10]);
5186 g_free(name);
5187
5188 return buf; 5226 return buf;
5189 } else { 5227 } else {
5190 char *str = buf; 5228 char *str = buf;
5191 gboolean isset; 5229 gboolean isset;
5192 int ivalue = 0; 5230 int ivalue = 0;
5245 empty = FALSE; 5283 empty = FALSE;
5246 } 5284 }
5247 } 5285 }
5248 5286
5249 g_snprintf(str, sizeof(buf) - (str - buf), "'>"); 5287 g_snprintf(str, sizeof(buf) - (str - buf), "'>");
5250 g_free(name);
5251 5288
5252 return (empty ? "" : buf); 5289 return (empty ? "" : buf);
5253 } 5290 }
5254 } 5291 }
5255 5292
5256 static const gchar *tag_to_html_end(GtkTextTag *tag) 5293 static const gchar *tag_to_html_end(GtkTextTag *tag)
5257 { 5294 {
5258 gchar *name; 5295 const gchar *name;
5259 5296
5297 name = tag->name;
5260 g_return_val_if_fail(name != NULL, ""); 5298 g_return_val_if_fail(name != NULL, "");
5261 g_object_get(G_OBJECT(tag), "name", &name, NULL);
5262 5299
5263 if (strcmp(name, "BOLD") == 0) { 5300 if (strcmp(name, "BOLD") == 0) {
5264 g_free(name);
5265 return "</b>"; 5301 return "</b>";
5266 } else if (strcmp(name, "ITALICS") == 0) { 5302 } else if (strcmp(name, "ITALICS") == 0) {
5267 g_free(name);
5268 return "</i>"; 5303 return "</i>";
5269 } else if (strcmp(name, "UNDERLINE") == 0) { 5304 } else if (strcmp(name, "UNDERLINE") == 0) {
5270 g_free(name);
5271 return "</u>"; 5305 return "</u>";
5272 } else if (strcmp(name, "STRIKE") == 0) { 5306 } else if (strcmp(name, "STRIKE") == 0) {
5273 g_free(name);
5274 return "</s>"; 5307 return "</s>";
5275 } else if (strncmp(name, "LINK ", 5) == 0) { 5308 } else if (strncmp(name, "LINK ", 5) == 0) {
5276 g_free(name);
5277 return "</a>"; 5309 return "</a>";
5278 } else if (strncmp(name, "FORECOLOR ", 10) == 0) { 5310 } else if (strncmp(name, "FORECOLOR ", 10) == 0) {
5279 g_free(name);
5280 return "</font>"; 5311 return "</font>";
5281 } else if (strncmp(name, "BACKCOLOR ", 10) == 0) { 5312 } else if (strncmp(name, "BACKCOLOR ", 10) == 0) {
5282 g_free(name);
5283 return "</font>"; 5313 return "</font>";
5284 } else if (strncmp(name, "BACKGROUND ", 10) == 0) { 5314 } else if (strncmp(name, "BACKGROUND ", 10) == 0) {
5285 g_free(name);
5286 return "</body>"; 5315 return "</body>";
5287 } else if (strncmp(name, "FONT FACE ", 10) == 0) { 5316 } else if (strncmp(name, "FONT FACE ", 10) == 0) {
5288 g_free(name);
5289 return "</font>"; 5317 return "</font>";
5290 } else if (strncmp(name, "FONT SIZE ", 10) == 0) { 5318 } else if (strncmp(name, "FONT SIZE ", 10) == 0) {
5291 g_free(name);
5292 return "</font>"; 5319 return "</font>";
5293 } else { 5320 } else {
5294 const char *props[] = {"weight-set", "foreground-set", "background-set", 5321 const char *props[] = {"weight-set", "foreground-set", "background-set",
5295 "size-set", "underline-set", NULL}; 5322 "size-set", "underline-set", NULL};
5296 int i; 5323 int i;
5298 gboolean set = FALSE; 5325 gboolean set = FALSE;
5299 g_object_get(G_OBJECT(tag), props[i], &set, NULL); 5326 g_object_get(G_OBJECT(tag), props[i], &set, NULL);
5300 if (set) 5327 if (set)
5301 return "</span>"; 5328 return "</span>";
5302 } 5329 }
5303
5304 g_free(name);
5305 5330
5306 return ""; 5331 return "";
5307 } 5332 }
5308 } 5333 }
5309 5334