Mercurial > pidgin.yaz
comparison src/gtkimhtml.c @ 4895:9e50494f63a1
[gaim-migrate @ 5227]
IM image support.
javabsp did most of the hard work, I just cleaned it up and fixed the bugs ;-)
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Wed, 26 Mar 2003 06:19:44 +0000 |
parents | dc6de8ad81ae |
children | 0bbccc88bc67 |
comparison
equal
deleted
inserted
replaced
4894:eea963dcd1ed | 4895:9e50494f63a1 |
---|---|
22 #ifdef HAVE_CONFIG_H | 22 #ifdef HAVE_CONFIG_H |
23 #include <config.h> | 23 #include <config.h> |
24 #endif | 24 #endif |
25 #include "gtkimhtml.h" | 25 #include "gtkimhtml.h" |
26 #include <gtk/gtk.h> | 26 #include <gtk/gtk.h> |
27 #include <glib/gerror.h> | |
27 #include <gdk/gdkkeysyms.h> | 28 #include <gdk/gdkkeysyms.h> |
28 #include <string.h> | 29 #include <string.h> |
29 #include <ctype.h> | 30 #include <ctype.h> |
30 #include <stdio.h> | 31 #include <stdio.h> |
31 #include <stdlib.h> | 32 #include <stdlib.h> |
52 | 53 |
53 #define TOOLTIP_TIMEOUT 500 | 54 #define TOOLTIP_TIMEOUT 500 |
54 | 55 |
55 static gboolean gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer user_data); | 56 static gboolean gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer user_data); |
56 | 57 |
57 | 58 static gboolean gtk_size_allocate_cb(GtkWidget *widget, GtkAllocation *alloc, gpointer user_data); |
58 static gint gtk_imhtml_tip (gpointer data); | 59 static gint gtk_imhtml_tip (gpointer data); |
60 | |
59 | 61 |
60 /* POINT_SIZE converts from AIM font sizes to point sizes. It probably should be redone in such a | 62 /* POINT_SIZE converts from AIM font sizes to point sizes. It probably should be redone in such a |
61 * way that it base the sizes off the default font size rather than using arbitrary font sizes. */ | 63 * way that it base the sizes off the default font size rather than using arbitrary font sizes. */ |
62 #define MAX_FONT_SIZE 7 | 64 #define MAX_FONT_SIZE 7 |
63 #define POINT_SIZE(x) (_point_sizes [MIN ((x), MAX_FONT_SIZE) - 1]) | 65 #define POINT_SIZE(x) (_point_sizes [MIN ((x), MAX_FONT_SIZE) - 1]) |
151 | 153 |
152 static void | 154 static void |
153 gtk_imhtml_finalize (GObject *object) | 155 gtk_imhtml_finalize (GObject *object) |
154 { | 156 { |
155 GtkIMHtml *imhtml = GTK_IMHTML(object); | 157 GtkIMHtml *imhtml = GTK_IMHTML(object); |
158 GList *scalables; | |
159 | |
156 g_hash_table_destroy(imhtml->smiley_data); | 160 g_hash_table_destroy(imhtml->smiley_data); |
157 gtk_smiley_tree_destroy(imhtml->default_smilies); | 161 gtk_smiley_tree_destroy(imhtml->default_smilies); |
158 gdk_cursor_unref(imhtml->hand_cursor); | 162 gdk_cursor_unref(imhtml->hand_cursor); |
159 gdk_cursor_unref(imhtml->arrow_cursor); | 163 gdk_cursor_unref(imhtml->arrow_cursor); |
160 if(imhtml->tip_window){ | 164 if(imhtml->tip_window){ |
161 gtk_widget_destroy(imhtml->tip_window); | 165 gtk_widget_destroy(imhtml->tip_window); |
162 } | 166 } |
163 if(imhtml->tip_timer) | 167 if(imhtml->tip_timer) |
164 gtk_timeout_remove(imhtml->tip_timer); | 168 gtk_timeout_remove(imhtml->tip_timer); |
165 | 169 |
170 for(scalables = imhtml->scalables; scalables; scalables = scalables->next) { | |
171 GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(scalables->data); | |
172 scale->free(scale); | |
173 } | |
174 | |
175 g_list_free(imhtml->scalables); | |
166 G_OBJECT_CLASS(parent_class)->finalize (object); | 176 G_OBJECT_CLASS(parent_class)->finalize (object); |
167 } | 177 } |
168 | 178 |
169 /* Boring GTK stuff */ | 179 /* Boring GTK stuff */ |
170 static void gtk_imhtml_class_init (GtkIMHtmlClass *class) | 180 static void gtk_imhtml_class_init (GtkIMHtmlClass *class) |
219 imhtml->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal, | 229 imhtml->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal, |
220 g_free, gtk_smiley_tree_destroy); | 230 g_free, gtk_smiley_tree_destroy); |
221 imhtml->default_smilies = gtk_smiley_tree_new(); | 231 imhtml->default_smilies = gtk_smiley_tree_new(); |
222 | 232 |
223 g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL); | 233 g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL); |
234 g_signal_connect(G_OBJECT(imhtml), "size-allocate", G_CALLBACK(gtk_size_allocate_cb), NULL); | |
224 | 235 |
225 imhtml->tip = NULL; | 236 imhtml->tip = NULL; |
226 imhtml->tip_timer = 0; | 237 imhtml->tip_timer = 0; |
227 imhtml->tip_window = NULL; | 238 imhtml->tip_window = NULL; |
239 | |
240 imhtml->scalables = NULL; | |
228 } | 241 } |
229 | 242 |
230 GtkWidget *gtk_imhtml_new(void *a, void *b) | 243 GtkWidget *gtk_imhtml_new(void *a, void *b) |
231 { | 244 { |
232 return GTK_WIDGET(g_object_new(gtk_imhtml_get_type(), NULL)); | 245 return GTK_WIDGET(g_object_new(gtk_imhtml_get_type(), NULL)); |
717 } | 730 } |
718 | 731 |
719 | 732 |
720 | 733 |
721 #define NEW_TEXT_BIT 0 | 734 #define NEW_TEXT_BIT 0 |
722 #define NEW_HR_BIT 1 | |
723 #define NEW_COMMENT_BIT 2 | 735 #define NEW_COMMENT_BIT 2 |
736 #define NEW_SCALABLE_BIT 1 | |
724 #define NEW_BIT(x) ws [wpos] = '\0'; \ | 737 #define NEW_BIT(x) ws [wpos] = '\0'; \ |
725 mark2 = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); \ | 738 mark2 = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); \ |
726 gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, -1); \ | 739 gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, -1); \ |
727 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ | 740 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ |
728 gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, mark2); \ | 741 gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, mark2); \ |
729 gtk_text_buffer_delete_mark(imhtml->text_buffer, mark2); \ | 742 gtk_text_buffer_delete_mark(imhtml->text_buffer, mark2); \ |
730 if (bold) \ | 743 if (bold) \ |
731 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", &siter, &iter); \ | 744 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", &siter, &iter); \ |
732 if (italics) \ | 745 if (italics) \ |
733 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", &siter, &iter); \ | 746 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", &siter, &iter); \ |
734 if (underline) \ | 747 if (underline) \ |
735 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &siter, &iter); \ | 748 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &siter, &iter); \ |
736 if (strike) \ | 749 if (strike) \ |
737 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "STRIKE", &siter, &iter); \ | 750 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "STRIKE", &siter, &iter); \ |
773 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ | 786 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ |
774 } \ | 787 } \ |
775 wpos = 0; \ | 788 wpos = 0; \ |
776 ws[0] = 0; \ | 789 ws[0] = 0; \ |
777 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ | 790 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ |
778 if (x == NEW_HR_BIT) { \ | 791 if (x == NEW_SCALABLE_BIT) { \ |
779 GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, &iter); \ | 792 GdkRectangle rect; \ |
780 GtkWidget *sep = gtk_hseparator_new(); \ | 793 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); \ |
781 GdkRectangle rect; \ | 794 scalable->add_to(scalable, imhtml, &iter); \ |
782 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); \ | 795 scalable->scale(scalable, rect.width, rect.height); \ |
783 gtk_widget_set_size_request(GTK_WIDGET(sep), rect.width, 2); \ | 796 imhtml->scalables = g_list_append(imhtml->scalables, scalable); \ |
784 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), sep, anchor); \ | 797 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ |
785 gtk_widget_show(sep); \ | |
786 } \ | 798 } \ |
799 | |
800 | |
787 | 801 |
788 GString* gtk_imhtml_append_text (GtkIMHtml *imhtml, | 802 GString* gtk_imhtml_append_text (GtkIMHtml *imhtml, |
789 const gchar *text, | 803 const gchar *text, |
790 gint len, | 804 gint len, |
791 GtkIMHtmlOptions options) | 805 GtkIMHtmlOptions options) |
816 GSList *fonts = NULL; | 830 GSList *fonts = NULL; |
817 | 831 |
818 GdkRectangle rect; | 832 GdkRectangle rect; |
819 int y, height; | 833 int y, height; |
820 | 834 |
835 GtkIMHtmlScalable *scalable = NULL; | |
836 | |
821 g_return_val_if_fail (imhtml != NULL, NULL); | 837 g_return_val_if_fail (imhtml != NULL, NULL); |
822 g_return_val_if_fail (GTK_IS_IMHTML (imhtml), NULL); | 838 g_return_val_if_fail (GTK_IS_IMHTML (imhtml), NULL); |
823 g_return_val_if_fail (text != NULL, NULL); | 839 g_return_val_if_fail (text != NULL, NULL); |
824 g_return_val_if_fail (len != 0, NULL); | 840 g_return_val_if_fail (len != 0, NULL); |
825 | 841 |
939 NEW_BIT (NEW_TEXT_BIT); | 955 NEW_BIT (NEW_TEXT_BIT); |
940 break; | 956 break; |
941 case 26: /* HR */ | 957 case 26: /* HR */ |
942 case 42: /* HR (opt) */ | 958 case 42: /* HR (opt) */ |
943 ws[wpos++] = '\n'; | 959 ws[wpos++] = '\n'; |
944 NEW_BIT(NEW_HR_BIT); | 960 scalable = gaim_hr_new(); |
961 NEW_BIT(NEW_SCALABLE_BIT); | |
945 ws[wpos++] = '\n'; | 962 ws[wpos++] = '\n'; |
946 break; | 963 break; |
947 case 27: /* /FONT */ | 964 case 27: /* /FONT */ |
948 if (fonts) { | 965 if (fonts) { |
949 FontDetail *font = fonts->data; | 966 FontDetail *font = fonts->data; |
978 case 37: /* FONT */ | 995 case 37: /* FONT */ |
979 case 38: /* HEAD */ | 996 case 38: /* HEAD */ |
980 case 39: /* /HEAD */ | 997 case 39: /* /HEAD */ |
981 break; | 998 break; |
982 case 40: /* BINARY */ | 999 case 40: /* BINARY */ |
1000 NEW_BIT (NEW_TEXT_BIT); | |
1001 break; | |
983 case 41: /* /BINARY */ | 1002 case 41: /* /BINARY */ |
984 break; | 1003 break; |
985 case 43: /* FONT (opt) */ | 1004 case 43: /* FONT (opt) */ |
986 { | 1005 { |
987 gchar *color, *back, *face, *size, *sml; | 1006 gchar *color, *back, *face, *size, *sml; |
1060 g_free (url); | 1079 g_free (url); |
1061 url = href; | 1080 url = href; |
1062 } | 1081 } |
1063 } | 1082 } |
1064 break; | 1083 break; |
1084 case 46: /* IMG (opt) */ | |
1085 { | |
1086 gchar *src = gtk_imhtml_get_html_opt (tag, "SRC="); | |
1087 gchar *id = gtk_imhtml_get_html_opt (tag, "ID="); | |
1088 gchar *datasize = gtk_imhtml_get_html_opt (tag, "DATASIZE="); | |
1089 gint im_len = datasize?atoi(datasize):0; | |
1090 | |
1091 if (src && id && im_len && im_len <= len - pos) { | |
1092 /* This is an embedded IM image, or is it? */ | |
1093 char *tmp = NULL; | |
1094 const char *alltext; | |
1095 guchar *imagedata = NULL; | |
1096 | |
1097 GdkPixbufLoader *load; | |
1098 GdkPixbuf *imagepb = NULL; | |
1099 GError *error = NULL; | |
1100 | |
1101 tmp = g_strdup_printf("<DATA ID=\"%s\" SIZE=\"%s\">", id, datasize); | |
1102 alltext = strstr(c, tmp); | |
1103 imagedata = g_memdup(alltext + strlen(tmp), im_len); | |
1104 | |
1105 g_free(tmp); | |
1106 | |
1107 load = gdk_pixbuf_loader_new(); | |
1108 if (!gdk_pixbuf_loader_write(load, imagedata, im_len, &error)){ | |
1109 fprintf(stderr, "IM Image corrupted or unreadable.: %s\n", error->message); | |
1110 } else { | |
1111 imagepb = gdk_pixbuf_loader_get_pixbuf(load); | |
1112 if (imagepb) { | |
1113 scalable = gaim_im_image_new(imagepb); | |
1114 NEW_BIT(NEW_SCALABLE_BIT); | |
1115 } | |
1116 } | |
1117 | |
1118 gdk_pixbuf_loader_close(load, NULL); | |
1119 | |
1120 | |
1121 g_free(imagedata); | |
1122 g_free(id); | |
1123 g_free(datasize); | |
1124 g_free(src); | |
1125 | |
1126 break; | |
1127 } | |
1128 g_free(id); | |
1129 g_free(datasize); | |
1130 g_free(src); | |
1131 } | |
1065 case 47: /* P (opt) */ | 1132 case 47: /* P (opt) */ |
1066 case 48: /* H3 (opt) */ | 1133 case 48: /* H3 (opt) */ |
1067 break; | 1134 break; |
1068 case 49: /* comment */ | 1135 case 49: /* comment */ |
1069 NEW_BIT (NEW_TEXT_BIT); | 1136 NEW_BIT (NEW_TEXT_BIT); |
1301 gtk_widget_set_size_request (imhtml->tip_window, w, h); | 1368 gtk_widget_set_size_request (imhtml->tip_window, w, h); |
1302 gtk_widget_show (imhtml->tip_window); | 1369 gtk_widget_show (imhtml->tip_window); |
1303 gtk_window_move (GTK_WINDOW(imhtml->tip_window), x, y); | 1370 gtk_window_move (GTK_WINDOW(imhtml->tip_window), x, y); |
1304 | 1371 |
1305 pango_font_metrics_unref(font); | 1372 pango_font_metrics_unref(font); |
1306 g_object_unref (layout); | 1373 g_object_unref(layout); |
1307 | 1374 |
1308 return FALSE; | 1375 return FALSE; |
1309 } | 1376 } |
1377 | |
1378 static gboolean gtk_size_allocate_cb(GtkWidget *widget, GtkAllocation *alloc, gpointer user_data) | |
1379 { | |
1380 static GdkRectangle old_rect = {0, 0, 0, 0}; | |
1381 GdkRectangle rect; | |
1382 | |
1383 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &rect); | |
1384 | |
1385 if(old_rect.width && (old_rect.width != rect.width || old_rect.height != rect.height)){ | |
1386 GList *iter = GTK_IMHTML(widget)->scalables; | |
1387 | |
1388 while(iter){ | |
1389 GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(iter->data); | |
1390 scale->scale(scale, rect.width, rect.height); | |
1391 | |
1392 iter = iter->next; | |
1393 } | |
1394 } | |
1395 | |
1396 old_rect = rect; | |
1397 return FALSE; | |
1398 } | |
1399 | |
1400 /* GtkIMHtmlScalable, gaim_im_image, gaim_hr */ | |
1401 GtkIMHtmlScalable *gaim_im_image_new(GdkPixbuf *img) | |
1402 { | |
1403 gaim_im_image *im_image = g_malloc(sizeof(gaim_im_image)); | |
1404 | |
1405 GTK_IMHTML_SCALABLE(im_image)->scale = gaim_im_image_scale; | |
1406 GTK_IMHTML_SCALABLE(im_image)->add_to = gaim_im_image_add_to; | |
1407 GTK_IMHTML_SCALABLE(im_image)->free = gaim_im_image_free; | |
1408 im_image->image = img; | |
1409 im_image->width = gdk_pixbuf_get_width(img); | |
1410 im_image->height = gdk_pixbuf_get_height(img); | |
1411 im_image->imhtml = NULL; | |
1412 im_image->mark = NULL; | |
1413 | |
1414 return GTK_IMHTML_SCALABLE(im_image); | |
1415 } | |
1416 | |
1417 void gaim_im_image_scale(GtkIMHtmlScalable *scale, int width, int height) | |
1418 { | |
1419 gaim_im_image *image = (gaim_im_image *)scale; | |
1420 | |
1421 if(image->width > width || image->height > height){ | |
1422 GdkPixbuf *new_image = NULL; | |
1423 GtkTextIter start, end; | |
1424 float factor; | |
1425 int new_width = image->width, new_height = image->height; | |
1426 | |
1427 gtk_text_buffer_get_iter_at_mark(image->imhtml->text_buffer, &start, image->mark); | |
1428 end = start; | |
1429 gtk_text_iter_forward_char(&end); | |
1430 gtk_text_buffer_delete(image->imhtml->text_buffer, &start, &end); | |
1431 | |
1432 if(image->width > width){ | |
1433 factor = (float)(width)/image->width; | |
1434 new_width = width; | |
1435 new_height = image->height * factor; | |
1436 } | |
1437 if(new_height > height){ | |
1438 factor = (float)(height)/new_height; | |
1439 new_height = height; | |
1440 new_width = new_width * factor; | |
1441 } | |
1442 | |
1443 gtk_text_buffer_get_iter_at_mark(image->imhtml->text_buffer, &start, image->mark); | |
1444 new_image = gdk_pixbuf_scale_simple(image->image, new_width, new_height, GDK_INTERP_BILINEAR); | |
1445 gtk_text_buffer_insert_pixbuf(image->imhtml->text_buffer, &start, new_image); | |
1446 | |
1447 g_object_unref(G_OBJECT(new_image)); | |
1448 } | |
1449 } | |
1450 | |
1451 void gaim_im_image_free(GtkIMHtmlScalable *scale) | |
1452 { | |
1453 gaim_im_image *image = (gaim_im_image *)scale; | |
1454 | |
1455 g_object_unref(image->image); | |
1456 g_free(scale); | |
1457 } | |
1458 | |
1459 void gaim_im_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter) | |
1460 { | |
1461 gaim_im_image *image = (gaim_im_image *)scale; | |
1462 | |
1463 image->mark = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, iter, TRUE); | |
1464 gtk_text_buffer_insert_pixbuf(imhtml->text_buffer, iter, image->image); | |
1465 image->imhtml = imhtml; | |
1466 } | |
1467 | |
1468 GtkIMHtmlScalable *gaim_hr_new() | |
1469 { | |
1470 gaim_hr *hr = g_malloc(sizeof(gaim_hr)); | |
1471 | |
1472 GTK_IMHTML_SCALABLE(hr)->scale = gaim_hr_scale; | |
1473 GTK_IMHTML_SCALABLE(hr)->add_to = gaim_hr_add_to; | |
1474 GTK_IMHTML_SCALABLE(hr)->free = gaim_hr_free; | |
1475 | |
1476 hr->sep = gtk_hseparator_new(); | |
1477 gtk_widget_set_size_request(hr->sep, 5000, 2); | |
1478 gtk_widget_show(hr->sep); | |
1479 | |
1480 return GTK_IMHTML_SCALABLE(hr); | |
1481 } | |
1482 | |
1483 void gaim_hr_scale(GtkIMHtmlScalable *scale, int width, int height) | |
1484 { | |
1485 gtk_widget_set_size_request(((gaim_hr *)scale)->sep, width, 2); | |
1486 } | |
1487 | |
1488 void gaim_hr_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter) | |
1489 { | |
1490 gaim_hr *hr = (gaim_hr *)scale; | |
1491 GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter); | |
1492 | |
1493 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), hr->sep, anchor); | |
1494 } | |
1495 | |
1496 void gaim_hr_free(GtkIMHtmlScalable *scale) | |
1497 { | |
1498 /* gtk_widget_destroy(((gaim_hr *)scale)->sep); */ | |
1499 g_free(scale); | |
1500 } |