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 }