comparison src/gtkimhtml.c @ 5967:156953fe3e14

[gaim-migrate @ 6414] I gave gtkimhtml some TLC. It no longer contains the word "Gaim," and it doesn't complain about needing to include a header. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sat, 28 Jun 2003 00:49:56 +0000
parents 5fb6bd688a5b
children 460b4ba2d452
comparison
equal deleted inserted replaced
5966:5fb6bd688a5b 5967:156953fe3e14
56 #define GTK_WRAP_WORD_CHAR GTK_WRAP_WORD 56 #define GTK_WRAP_WORD_CHAR GTK_WRAP_WORD
57 #endif 57 #endif
58 58
59 #define TOOLTIP_TIMEOUT 500 59 #define TOOLTIP_TIMEOUT 500
60 60
61 static gboolean gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer user_data);
62 static gboolean gtk_leave_event_notify(GtkWidget *imhtml, GdkEventCrossing *event, gpointer user_data);
63
64 static gboolean gtk_size_allocate_cb(GtkIMHtml *widget, GtkAllocation *alloc, gpointer user_data);
65
66 static gboolean gaim_im_image_clicked(GtkWidget *w, GdkEvent *event, gaim_im_image *image);
67
68 static gint gtk_imhtml_tip (gpointer data);
69
70
71 /* POINT_SIZE converts from AIM font sizes to point sizes. It probably should be redone in such a 61 /* POINT_SIZE converts from AIM font sizes to point sizes. It probably should be redone in such a
72 * way that it base the sizes off the default font size rather than using arbitrary font sizes. */ 62 * way that it base the sizes off the default font size rather than using arbitrary font sizes. */
73 #define MAX_FONT_SIZE 7 63 #define MAX_FONT_SIZE 7
74 #define POINT_SIZE(x) (options & GTK_IMHTML_USE_POINTSIZE ? x : _point_sizes [MIN ((x), MAX_FONT_SIZE) - 1]) 64 #define POINT_SIZE(x) (options & GTK_IMHTML_USE_POINTSIZE ? x : _point_sizes [MIN ((x), MAX_FONT_SIZE) - 1])
75 static gint _point_sizes [] = { 8, 10, 12, 14, 20, 30, 40 }; 65 static gint _point_sizes [] = { 8, 10, 12, 14, 20, 30, 40 };
76
77 /* The four elements present in a <FONT> tag contained in a struct */
78 typedef struct _FontDetail FontDetail;
79 struct _FontDetail {
80 gushort size;
81 gchar *face;
82 gchar *fore;
83 gchar *back;
84 gchar *sml;
85 };
86
87 struct _GtkSmileyTree {
88 GString *values;
89 GtkSmileyTree **children;
90 GtkIMHtmlSmiley *image;
91 };
92 66
93 static GtkSmileyTree* 67 static GtkSmileyTree*
94 gtk_smiley_tree_new () 68 gtk_smiley_tree_new ()
95 { 69 {
96 return g_new0 (GtkSmileyTree, 1); 70 return g_new0 (GtkSmileyTree, 1);
147 } 121 }
148 g_free (t); 122 g_free (t);
149 } 123 }
150 } 124 }
151 125
126 static gboolean gtk_size_allocate_cb(GtkIMHtml *widget, GtkAllocation *alloc, gpointer user_data)
127 {
128 GdkRectangle rect;
129
130 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &rect);
131 if(widget->old_rect.width != rect.width || widget->old_rect.height != rect.height){
132 GList *iter = GTK_IMHTML(widget)->scalables;
133
134 while(iter){
135 GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(iter->data);
136 scale->scale(scale, rect.width, rect.height);
137
138 iter = iter->next;
139 }
140 }
141
142 widget->old_rect = rect;
143 return FALSE;
144 }
145
146 static gint
147 gtk_imhtml_tip_paint (GtkIMHtml *imhtml)
148 {
149 PangoLayout *layout;
150
151 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
152
153 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
154
155 gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window,
156 GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, imhtml->tip_window,
157 "tooltip", 0, 0, -1, -1);
158
159 gtk_paint_layout (imhtml->tip_window->style, imhtml->tip_window->window, GTK_STATE_NORMAL,
160 FALSE, NULL, imhtml->tip_window, NULL, 4, 4, layout);
161
162 g_object_unref(layout);
163 return FALSE;
164 }
165
166 static gint
167 gtk_imhtml_tip (gpointer data)
168 {
169 GtkIMHtml *imhtml = data;
170 PangoFontMetrics *font;
171 PangoLayout *layout;
172
173 gint gap, x, y, h, w, scr_w, baseline_skip;
174
175 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
176
177 if (!imhtml->tip || !GTK_WIDGET_DRAWABLE (GTK_WIDGET(imhtml))) {
178 imhtml->tip_timer = 0;
179 return FALSE;
180 }
181
182 if (imhtml->tip_window){
183 gtk_widget_destroy (imhtml->tip_window);
184 imhtml->tip_window = NULL;
185 }
186
187 imhtml->tip_timer = 0;
188 imhtml->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
189 gtk_widget_set_app_paintable (imhtml->tip_window, TRUE);
190 gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
191 gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
192 g_signal_connect_swapped (G_OBJECT (imhtml->tip_window), "expose_event",
193 G_CALLBACK (gtk_imhtml_tip_paint), imhtml);
194
195 gtk_widget_ensure_style (imhtml->tip_window);
196 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
197 font = pango_font_get_metrics(pango_context_load_font(pango_layout_get_context(layout),
198 imhtml->tip_window->style->font_desc),
199 NULL);
200
201
202 pango_layout_get_pixel_size(layout, &scr_w, NULL);
203 gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font) +
204 pango_font_metrics_get_descent(font))/ 4);
205
206 if (gap < 2)
207 gap = 2;
208 baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font) +
209 pango_font_metrics_get_descent(font));
210 w = 8 + scr_w;
211 h = 8 + baseline_skip;
212
213 gdk_window_get_pointer (NULL, &x, &y, NULL);
214 if (GTK_WIDGET_NO_WINDOW (GTK_WIDGET(imhtml)))
215 y += GTK_WIDGET(imhtml)->allocation.y;
216
217 scr_w = gdk_screen_width();
218
219 x -= ((w >> 1) + 4);
220
221 if ((x + w) > scr_w)
222 x -= (x + w) - scr_w;
223 else if (x < 0)
224 x = 0;
225
226 y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font) +
227 pango_font_metrics_get_descent(font));
228
229 gtk_widget_set_size_request (imhtml->tip_window, w, h);
230 gtk_widget_show (imhtml->tip_window);
231 gtk_window_move (GTK_WINDOW(imhtml->tip_window), x, y);
232
233 pango_font_metrics_unref(font);
234 g_object_unref(layout);
235
236 return FALSE;
237 }
238
239 gboolean gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer data)
240 {
241 GtkTextIter iter;
242 GdkWindow *win = event->window;
243 int x, y;
244 char *tip = NULL;
245 GSList *tags = NULL, *templist = NULL;
246 gdk_window_get_pointer(GTK_WIDGET(imhtml)->window, NULL, NULL, NULL);
247 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml), GTK_TEXT_WINDOW_WIDGET,
248 event->x, event->y, &x, &y);
249 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y);
250 tags = gtk_text_iter_get_tags(&iter);
251
252 templist = tags;
253 while (templist) {
254 GtkTextTag *tag = templist->data;
255 tip = g_object_get_data(G_OBJECT(tag), "link_url");
256 if (tip)
257 break;
258 templist = templist->next;
259 }
260
261 if (GTK_IMHTML(imhtml)->tip) {
262 if ((tip == GTK_IMHTML(imhtml)->tip)) {
263 return FALSE;
264 }
265 /* We've left the cell. Remove the timeout and create a new one below */
266 if (GTK_IMHTML(imhtml)->tip_window) {
267 gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
268 GTK_IMHTML(imhtml)->tip_window = NULL;
269 }
270 gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->arrow_cursor);
271 if (GTK_IMHTML(imhtml)->tip_timer)
272 g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
273 GTK_IMHTML(imhtml)->tip_timer = 0;
274 }
275
276 if(tip){
277 gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor);
278 GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT,
279 gtk_imhtml_tip, imhtml);
280 }
281
282 GTK_IMHTML(imhtml)->tip = tip;
283 g_slist_free(tags);
284 return FALSE;
285 }
286
287 gboolean gtk_leave_event_notify(GtkWidget *imhtml, GdkEventCrossing *event, gpointer data)
288 {
289 /* when leaving the widget, clear any current & pending tooltips and restore the cursor */
290 if (GTK_IMHTML(imhtml)->tip_window) {
291 gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
292 GTK_IMHTML(imhtml)->tip_window = NULL;
293 }
294 if (GTK_IMHTML(imhtml)->tip_timer) {
295 g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
296 GTK_IMHTML(imhtml)->tip_timer = 0;
297 }
298 gdk_window_set_cursor(event->window, GTK_IMHTML(imhtml)->arrow_cursor);
299
300 /* propogate the event normally */
301 return FALSE;
302 }
303
304
152 305
153 static GtkTextViewClass *parent_class = NULL; 306 static GtkTextViewClass *parent_class = NULL;
154
155 307
156 /* GtkIMHtml has one signal--URL_CLICKED */ 308 /* GtkIMHtml has one signal--URL_CLICKED */
157 enum { 309 enum {
158 URL_CLICKED, 310 URL_CLICKED,
159 LAST_SIGNAL 311 LAST_SIGNAL
368 be caught by the regular GtkTextView menu */ 520 be caught by the regular GtkTextView menu */
369 else 521 else
370 return FALSE; /* Let clicks go through if we didn't catch anything */ 522 return FALSE; /* Let clicks go through if we didn't catch anything */
371 } 523 }
372 524
373 gboolean gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer data)
374 {
375 GtkTextIter iter;
376 GdkWindow *win = event->window;
377 int x, y;
378 char *tip = NULL;
379 GSList *tags = NULL, *templist = NULL;
380 gdk_window_get_pointer(GTK_WIDGET(imhtml)->window, NULL, NULL, NULL);
381 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml), GTK_TEXT_WINDOW_WIDGET,
382 event->x, event->y, &x, &y);
383 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y);
384 tags = gtk_text_iter_get_tags(&iter);
385
386 templist = tags;
387 while (templist) {
388 GtkTextTag *tag = templist->data;
389 tip = g_object_get_data(G_OBJECT(tag), "link_url");
390 if (tip)
391 break;
392 templist = templist->next;
393 }
394
395 if (GTK_IMHTML(imhtml)->tip) {
396 if ((tip == GTK_IMHTML(imhtml)->tip)) {
397 return FALSE;
398 }
399 /* We've left the cell. Remove the timeout and create a new one below */
400 if (GTK_IMHTML(imhtml)->tip_window) {
401 gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
402 GTK_IMHTML(imhtml)->tip_window = NULL;
403 }
404 gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->arrow_cursor);
405 if (GTK_IMHTML(imhtml)->tip_timer)
406 g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
407 GTK_IMHTML(imhtml)->tip_timer = 0;
408 }
409
410 if(tip){
411 gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor);
412 GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT,
413 gtk_imhtml_tip, imhtml);
414 }
415
416 GTK_IMHTML(imhtml)->tip = tip;
417 g_slist_free(tags);
418 return FALSE;
419 }
420
421 gboolean gtk_leave_event_notify(GtkWidget *imhtml, GdkEventCrossing *event, gpointer data)
422 {
423 /* when leaving the widget, clear any current & pending tooltips and restore the cursor */
424 if (GTK_IMHTML(imhtml)->tip_window) {
425 gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
426 GTK_IMHTML(imhtml)->tip_window = NULL;
427 }
428 if (GTK_IMHTML(imhtml)->tip_timer) {
429 g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
430 GTK_IMHTML(imhtml)->tip_timer = 0;
431 }
432 gdk_window_set_cursor(event->window, GTK_IMHTML(imhtml)->arrow_cursor);
433
434 /* propogate the event normally */
435 return FALSE;
436 }
437
438 /* this isn't used yet 525 /* this isn't used yet
439 static void 526 static void
440 gtk_smiley_tree_remove (GtkSmileyTree *tree, 527 gtk_smiley_tree_remove (GtkSmileyTree *tree,
441 GtkIMHtmlSmiley *smiley) 528 GtkIMHtmlSmiley *smiley)
442 { 529 {
520 GSList *fonts, 607 GSList *fonts,
521 const gchar *text, 608 const gchar *text,
522 gint *len) 609 gint *len)
523 { 610 {
524 GtkSmileyTree *tree; 611 GtkSmileyTree *tree;
525 FontDetail *font; 612 GtkIMHtmlFontDetail *font;
526 char *sml = NULL; 613 char *sml = NULL;
527 614
528 if (fonts) { 615 if (fonts) {
529 font = fonts->data; 616 font = fonts->data;
530 sml = font->sml; 617 sml = font->sml;
835 if (bg) { \ 922 if (bg) { \
836 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", bg, NULL); \ 923 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", bg, NULL); \
837 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ 924 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
838 } \ 925 } \
839 if (fonts) { \ 926 if (fonts) { \
840 FontDetail *fd = fonts->data; \ 927 GtkIMHtmlFontDetail *fd = fonts->data; \
841 if (fd->fore) { \ 928 if (fd->fore) { \
842 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", fd->fore, NULL); \ 929 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", fd->fore, NULL); \
843 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ 930 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
844 } \ 931 } \
845 if (fd->back) { \ 932 if (fd->back) { \
1038 NEW_BIT (NEW_TEXT_BIT); 1125 NEW_BIT (NEW_TEXT_BIT);
1039 break; 1126 break;
1040 case 26: /* HR */ 1127 case 26: /* HR */
1041 case 42: /* HR (opt) */ 1128 case 42: /* HR (opt) */
1042 ws[wpos++] = '\n'; 1129 ws[wpos++] = '\n';
1043 scalable = gaim_hr_new(); 1130 scalable = gtk_imhtml_hr_new();
1044 NEW_BIT(NEW_SCALABLE_BIT); 1131 NEW_BIT(NEW_SCALABLE_BIT);
1045 ws[wpos++] = '\n'; 1132 ws[wpos++] = '\n';
1046 break; 1133 break;
1047 case 27: /* /FONT */ 1134 case 27: /* /FONT */
1048 if (fonts) { 1135 if (fonts) {
1049 FontDetail *font = fonts->data; 1136 GtkIMHtmlFontDetail *font = fonts->data;
1050 NEW_BIT (NEW_TEXT_BIT); 1137 NEW_BIT (NEW_TEXT_BIT);
1051 fonts = g_slist_remove (fonts, font); 1138 fonts = g_slist_remove (fonts, font);
1052 if (font->face) 1139 if (font->face)
1053 g_free (font->face); 1140 g_free (font->face);
1054 if (font->fore) 1141 if (font->fore)
1090 case 41: /* /BINARY */ 1177 case 41: /* /BINARY */
1091 break; 1178 break;
1092 case 43: /* FONT (opt) */ 1179 case 43: /* FONT (opt) */
1093 { 1180 {
1094 gchar *color, *back, *face, *size, *sml; 1181 gchar *color, *back, *face, *size, *sml;
1095 FontDetail *font, *oldfont = NULL; 1182 GtkIMHtmlFontDetail *font, *oldfont = NULL;
1096 color = gtk_imhtml_get_html_opt (tag, "COLOR="); 1183 color = gtk_imhtml_get_html_opt (tag, "COLOR=");
1097 back = gtk_imhtml_get_html_opt (tag, "BACK="); 1184 back = gtk_imhtml_get_html_opt (tag, "BACK=");
1098 face = gtk_imhtml_get_html_opt (tag, "FACE="); 1185 face = gtk_imhtml_get_html_opt (tag, "FACE=");
1099 size = gtk_imhtml_get_html_opt (tag, "SIZE="); 1186 size = gtk_imhtml_get_html_opt (tag, "SIZE=");
1100 sml = gtk_imhtml_get_html_opt (tag, "SML="); 1187 sml = gtk_imhtml_get_html_opt (tag, "SML=");
1101 if (!(color || back || face || size || sml)) 1188 if (!(color || back || face || size || sml))
1102 break; 1189 break;
1103 1190
1104 NEW_BIT (NEW_TEXT_BIT); 1191 NEW_BIT (NEW_TEXT_BIT);
1105 1192
1106 font = g_new0 (FontDetail, 1); 1193 font = g_new0 (GtkIMHtmlFontDetail, 1);
1107 if (fonts) 1194 if (fonts)
1108 oldfont = fonts->data; 1195 oldfont = fonts->data;
1109 1196
1110 if (color && !(options & GTK_IMHTML_NO_COLOURS)) 1197 if (color && !(options & GTK_IMHTML_NO_COLOURS))
1111 font->fore = color; 1198 font->fore = color;
1196 if (!gdk_pixbuf_loader_write(load, imagedata, im_len, &error)){ 1283 if (!gdk_pixbuf_loader_write(load, imagedata, im_len, &error)){
1197 fprintf(stderr, "IM Image corrupted or unreadable.: %s\n", error->message); 1284 fprintf(stderr, "IM Image corrupted or unreadable.: %s\n", error->message);
1198 } else { 1285 } else {
1199 imagepb = gdk_pixbuf_loader_get_pixbuf(load); 1286 imagepb = gdk_pixbuf_loader_get_pixbuf(load);
1200 if (imagepb) { 1287 if (imagepb) {
1201 scalable = gaim_im_image_new(imagepb, g_strdup(src)); 1288 scalable = gtk_imhtml_image_new(imagepb, g_strdup(src));
1202 NEW_BIT(NEW_SCALABLE_BIT); 1289 NEW_BIT(NEW_SCALABLE_BIT);
1203 g_object_unref(imagepb); 1290 g_object_unref(imagepb);
1204 } 1291 }
1205 } 1292 }
1206 1293
1250 NEW_BIT (NEW_TEXT_BIT); 1337 NEW_BIT (NEW_TEXT_BIT);
1251 } 1338 }
1252 c++; 1339 c++;
1253 pos++; 1340 pos++;
1254 } else if (imhtml->show_smileys && (gtk_imhtml_is_smiley (imhtml, fonts, c, &smilelen) || gtk_imhtml_is_smiley(imhtml, NULL, c, &smilelen))) { 1341 } else if (imhtml->show_smileys && (gtk_imhtml_is_smiley (imhtml, fonts, c, &smilelen) || gtk_imhtml_is_smiley(imhtml, NULL, c, &smilelen))) {
1255 FontDetail *fd; 1342 GtkIMHtmlFontDetail *fd;
1256 gchar *sml = NULL; 1343 gchar *sml = NULL;
1257 if (fonts) { 1344 if (fonts) {
1258 fd = fonts->data; 1345 fd = fonts->data;
1259 sml = fd->sml; 1346 sml = fd->sml;
1260 } 1347 }
1279 if (str) 1366 if (str)
1280 str = g_string_append (str, "</A>"); 1367 str = g_string_append (str, "</A>");
1281 } 1368 }
1282 1369
1283 while (fonts) { 1370 while (fonts) {
1284 FontDetail *font = fonts->data; 1371 GtkIMHtmlFontDetail *font = fonts->data;
1285 fonts = g_slist_remove (fonts, font); 1372 fonts = g_slist_remove (fonts, font);
1286 if (font->face) 1373 if (font->face)
1287 g_free (font->face); 1374 g_free (font->face);
1288 if (font->fore) 1375 if (font->fore)
1289 g_free (font->fore); 1376 g_free (font->fore);
1390 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x, 1477 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x,
1391 rect.y + rect.height); 1478 rect.y + rect.height);
1392 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0); 1479 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0);
1393 } 1480 }
1394 1481
1395 static gint 1482 /* GtkIMHtmlScalable, gtk_imhtml_image, gtk_imhtml_hr */
1396 gtk_imhtml_tip_paint (GtkIMHtml *imhtml) 1483 GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, gchar *filename)
1397 { 1484 {
1398 PangoLayout *layout; 1485 GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage));
1399
1400 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
1401
1402 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
1403
1404 gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window,
1405 GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, imhtml->tip_window,
1406 "tooltip", 0, 0, -1, -1);
1407
1408 gtk_paint_layout (imhtml->tip_window->style, imhtml->tip_window->window, GTK_STATE_NORMAL,
1409 FALSE, NULL, imhtml->tip_window, NULL, 4, 4, layout);
1410
1411 g_object_unref(layout);
1412 return FALSE;
1413 }
1414
1415 static gint
1416 gtk_imhtml_tip (gpointer data)
1417 {
1418 GtkIMHtml *imhtml = data;
1419 PangoFontMetrics *font;
1420 PangoLayout *layout;
1421
1422 gint gap, x, y, h, w, scr_w, baseline_skip;
1423
1424 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
1425
1426 if (!imhtml->tip || !GTK_WIDGET_DRAWABLE (GTK_WIDGET(imhtml))) {
1427 imhtml->tip_timer = 0;
1428 return FALSE;
1429 }
1430
1431 if (imhtml->tip_window){
1432 gtk_widget_destroy (imhtml->tip_window);
1433 imhtml->tip_window = NULL;
1434 }
1435
1436 imhtml->tip_timer = 0;
1437 imhtml->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
1438 gtk_widget_set_app_paintable (imhtml->tip_window, TRUE);
1439 gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
1440 gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
1441 g_signal_connect_swapped (G_OBJECT (imhtml->tip_window), "expose_event",
1442 G_CALLBACK (gtk_imhtml_tip_paint), imhtml);
1443
1444 gtk_widget_ensure_style (imhtml->tip_window);
1445 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
1446 font = pango_font_get_metrics(pango_context_load_font(pango_layout_get_context(layout),
1447 imhtml->tip_window->style->font_desc),
1448 NULL);
1449
1450
1451 pango_layout_get_pixel_size(layout, &scr_w, NULL);
1452 gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font) +
1453 pango_font_metrics_get_descent(font))/ 4);
1454
1455 if (gap < 2)
1456 gap = 2;
1457 baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font) +
1458 pango_font_metrics_get_descent(font));
1459 w = 8 + scr_w;
1460 h = 8 + baseline_skip;
1461
1462 gdk_window_get_pointer (NULL, &x, &y, NULL);
1463 if (GTK_WIDGET_NO_WINDOW (GTK_WIDGET(imhtml)))
1464 y += GTK_WIDGET(imhtml)->allocation.y;
1465
1466 scr_w = gdk_screen_width();
1467
1468 x -= ((w >> 1) + 4);
1469
1470 if ((x + w) > scr_w)
1471 x -= (x + w) - scr_w;
1472 else if (x < 0)
1473 x = 0;
1474
1475 y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font) +
1476 pango_font_metrics_get_descent(font));
1477
1478 gtk_widget_set_size_request (imhtml->tip_window, w, h);
1479 gtk_widget_show (imhtml->tip_window);
1480 gtk_window_move (GTK_WINDOW(imhtml->tip_window), x, y);
1481
1482 pango_font_metrics_unref(font);
1483 g_object_unref(layout);
1484
1485 return FALSE;
1486 }
1487
1488 static gboolean gtk_size_allocate_cb(GtkIMHtml *widget, GtkAllocation *alloc, gpointer user_data)
1489 {
1490 GdkRectangle rect;
1491
1492 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &rect);
1493 if(widget->old_rect.width != rect.width || widget->old_rect.height != rect.height){
1494 GList *iter = GTK_IMHTML(widget)->scalables;
1495
1496 while(iter){
1497 GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(iter->data);
1498 scale->scale(scale, rect.width, rect.height);
1499
1500 iter = iter->next;
1501 }
1502 }
1503
1504 widget->old_rect = rect;
1505 return FALSE;
1506 }
1507
1508 /* GtkIMHtmlScalable, gaim_im_image, gaim_hr */
1509 GtkIMHtmlScalable *gaim_im_image_new(GdkPixbuf *img, gchar *filename)
1510 {
1511 gaim_im_image *im_image = g_malloc(sizeof(gaim_im_image));
1512 GtkImage *image = GTK_IMAGE(gtk_image_new_from_pixbuf(img)); 1486 GtkImage *image = GTK_IMAGE(gtk_image_new_from_pixbuf(img));
1513 1487
1514 GTK_IMHTML_SCALABLE(im_image)->scale = gaim_im_image_scale; 1488 GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
1515 GTK_IMHTML_SCALABLE(im_image)->add_to = gaim_im_image_add_to; 1489 GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
1516 GTK_IMHTML_SCALABLE(im_image)->free = gaim_im_image_free; 1490 GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_image_free;
1517 1491
1518 im_image->pixbuf = img; 1492 im_image->pixbuf = img;
1519 im_image->image = image; 1493 im_image->image = image;
1520 im_image->width = gdk_pixbuf_get_width(img); 1494 im_image->width = gdk_pixbuf_get_width(img);
1521 im_image->height = gdk_pixbuf_get_height(img); 1495 im_image->height = gdk_pixbuf_get_height(img);
1524 1498
1525 g_object_ref(img); 1499 g_object_ref(img);
1526 return GTK_IMHTML_SCALABLE(im_image); 1500 return GTK_IMHTML_SCALABLE(im_image);
1527 } 1501 }
1528 1502
1529 void gaim_im_image_scale(GtkIMHtmlScalable *scale, int width, int height) 1503 void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height)
1530 { 1504 {
1531 gaim_im_image *image = (gaim_im_image *)scale; 1505 GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
1532 1506
1533 if(image->width > width || image->height > height){ 1507 if(image->width > width || image->height > height){
1534 GdkPixbuf *new_image = NULL; 1508 GdkPixbuf *new_image = NULL;
1535 float factor; 1509 float factor;
1536 int new_width = image->width, new_height = image->height; 1510 int new_width = image->width, new_height = image->height;
1550 gtk_image_set_from_pixbuf(image->image, new_image); 1524 gtk_image_set_from_pixbuf(image->image, new_image);
1551 g_object_unref(G_OBJECT(new_image)); 1525 g_object_unref(G_OBJECT(new_image));
1552 } 1526 }
1553 } 1527 }
1554 1528
1555 void gaim_im_image_free(GtkIMHtmlScalable *scale)
1556 {
1557 gaim_im_image *image = (gaim_im_image *)scale;
1558
1559 g_object_unref(image->pixbuf);
1560 g_free(image->filename);
1561 g_free(scale);
1562 }
1563
1564 void gaim_im_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
1565 {
1566 gaim_im_image *image = (gaim_im_image *)scale;
1567 GtkWidget *box = gtk_event_box_new();
1568 GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
1569
1570 gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(image->image));
1571
1572 gtk_widget_show(GTK_WIDGET(image->image));
1573 gtk_widget_show(box);
1574
1575 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), box, anchor);
1576 g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gaim_im_image_clicked), image);
1577 }
1578
1579 GtkIMHtmlScalable *gaim_hr_new()
1580 {
1581 gaim_hr *hr = g_malloc(sizeof(gaim_hr));
1582
1583 GTK_IMHTML_SCALABLE(hr)->scale = gaim_hr_scale;
1584 GTK_IMHTML_SCALABLE(hr)->add_to = gaim_hr_add_to;
1585 GTK_IMHTML_SCALABLE(hr)->free = gaim_hr_free;
1586
1587 hr->sep = gtk_hseparator_new();
1588 gtk_widget_set_size_request(hr->sep, 5000, 2);
1589 gtk_widget_show(hr->sep);
1590
1591 return GTK_IMHTML_SCALABLE(hr);
1592 }
1593
1594 void gaim_hr_scale(GtkIMHtmlScalable *scale, int width, int height)
1595 {
1596 gtk_widget_set_size_request(((gaim_hr *)scale)->sep, width, 2);
1597 }
1598
1599 void gaim_hr_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
1600 {
1601 gaim_hr *hr = (gaim_hr *)scale;
1602 GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
1603
1604 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), hr->sep, anchor);
1605 }
1606
1607 void gaim_hr_free(GtkIMHtmlScalable *scale)
1608 {
1609 /* gtk_widget_destroy(((gaim_hr *)scale)->sep); */
1610 g_free(scale);
1611 }
1612
1613 static void write_img_to_file(GtkWidget *w, GtkFileSelection *sel) 1529 static void write_img_to_file(GtkWidget *w, GtkFileSelection *sel)
1614 { 1530 {
1615 const gchar *filename = gtk_file_selection_get_filename(sel); 1531 const gchar *filename = gtk_file_selection_get_filename(sel);
1616 gaim_im_image *image = g_object_get_data(G_OBJECT(sel), "gaim_im_image"); 1532 gchar *dirname;
1533 GtkIMHtmlImage *image = g_object_get_data(G_OBJECT(sel), "GtkIMHtmlImage");
1617 gchar *type = NULL; 1534 gchar *type = NULL;
1618 GError *error = NULL; 1535 GError *error = NULL;
1619 #if GTK_CHECK_VERSION(2,2,0) 1536 #if GTK_CHECK_VERSION(2,2,0)
1620 GSList *formats = gdk_pixbuf_get_formats(); 1537 GSList *formats = gdk_pixbuf_get_formats();
1621 #endif 1538 #endif
1622 1539
1623 if (gaim_gtk_check_if_dir(filename, GTK_FILE_SELECTION(sel))) 1540 if (g_file_test(filename, G_FILE_TEST_IS_DIR)) {
1541 /* append a / if needed */
1542 if (filename[strlen(filename) - 1] != '/') {
1543 dirname = g_strconcat(filename, "/", NULL);
1544 } else {
1545 dirname = g_strdup(filename);
1546 }
1547 gtk_file_selection_set_filename(sel, dirname);
1548 g_free(dirname);
1624 return; 1549 return;
1550 }
1625 1551
1626 #if GTK_CHECK_VERSION(2,2,0) 1552 #if GTK_CHECK_VERSION(2,2,0)
1627 while(formats){ 1553 while(formats){
1628 GdkPixbufFormat *format = formats->data; 1554 GdkPixbufFormat *format = formats->data;
1629 gchar **extensions = gdk_pixbuf_format_get_extensions(format); 1555 gchar **extensions = gdk_pixbuf_format_get_extensions(format);
1668 1594
1669 /* If I can't find a valid type, I will just tell the user about it and then assume 1595 /* If I can't find a valid type, I will just tell the user about it and then assume
1670 it's a png */ 1596 it's a png */
1671 if(!type){ 1597 if(!type){
1672 gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, 1598 gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
1673 _("Gaim was unable to guess the image type base on the file extension supplied. Defaulting to PNG.")); 1599 _("Unable to guess the image type based on the file extension supplied. Defaulting to PNG."));
1674 type = g_strdup("png"); 1600 type = g_strdup("png");
1675 } 1601 }
1676 1602
1677 gdk_pixbuf_save(image->pixbuf, filename, type, &error, NULL); 1603 gdk_pixbuf_save(image->pixbuf, filename, type, &error, NULL);
1678 1604
1683 } 1609 }
1684 1610
1685 g_free(type); 1611 g_free(type);
1686 } 1612 }
1687 1613
1688 static void gaim_im_image_save(GtkWidget *w, gaim_im_image *image) 1614 static void gtk_imhtml_image_save(GtkWidget *w, GtkIMHtmlImage *image)
1689 { 1615 {
1690 GtkWidget *sel = gtk_file_selection_new(_("Gaim - Save Image")); 1616 GtkWidget *sel = gtk_file_selection_new(_("Save Image"));
1691 1617
1692 gtk_file_selection_set_filename(GTK_FILE_SELECTION(sel), image->filename); 1618 gtk_file_selection_set_filename(GTK_FILE_SELECTION(sel), image->filename);
1693 g_object_set_data(G_OBJECT(sel), "gaim_im_image", image); 1619 g_object_set_data(G_OBJECT(sel), "GtkIMHtmlImage", image);
1694 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(sel)->ok_button), "clicked", 1620 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(sel)->ok_button), "clicked",
1695 G_CALLBACK(write_img_to_file), sel); 1621 G_CALLBACK(write_img_to_file), sel);
1696 1622
1697 g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(sel)->ok_button), "clicked", 1623 g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(sel)->ok_button), "clicked",
1698 G_CALLBACK(gtk_widget_destroy), sel); 1624 G_CALLBACK(gtk_widget_destroy), sel);
1700 G_CALLBACK(gtk_widget_destroy), sel); 1626 G_CALLBACK(gtk_widget_destroy), sel);
1701 1627
1702 gtk_widget_show(sel); 1628 gtk_widget_show(sel);
1703 } 1629 }
1704 1630
1705 static gboolean gaim_im_image_clicked(GtkWidget *w, GdkEvent *event, gaim_im_image *image) 1631 static gboolean gtk_imhtml_image_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlImage *image)
1706 { 1632 {
1707 GdkEventButton *event_button = (GdkEventButton *) event; 1633 GdkEventButton *event_button = (GdkEventButton *) event;
1708 1634
1709 if (event->type == GDK_BUTTON_RELEASE) { 1635 if (event->type == GDK_BUTTON_RELEASE) {
1710 if(event_button->button == 3) { 1636 if(event_button->button == 3) {
1714 1640
1715 /* buttons and such */ 1641 /* buttons and such */
1716 img = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU); 1642 img = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU);
1717 item = gtk_image_menu_item_new_with_mnemonic(text); 1643 item = gtk_image_menu_item_new_with_mnemonic(text);
1718 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); 1644 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
1719 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gaim_im_image_save), image); 1645 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_image_save), image);
1720 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); 1646 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1721 1647
1722 gtk_widget_show_all(menu); 1648 gtk_widget_show_all(menu);
1723 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 1649 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1724 event_button->button, event_button->time); 1650 event_button->button, event_button->time);
1732 be caught by the regular GtkTextView menu */ 1658 be caught by the regular GtkTextView menu */
1733 else 1659 else
1734 return FALSE; /* Let clicks go through if we didn't catch anything */ 1660 return FALSE; /* Let clicks go through if we didn't catch anything */
1735 1661
1736 } 1662 }
1663 void gtk_imhtml_image_free(GtkIMHtmlScalable *scale)
1664 {
1665 GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
1666
1667 g_object_unref(image->pixbuf);
1668 g_free(image->filename);
1669 g_free(scale);
1670 }
1671
1672 void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
1673 {
1674 GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
1675 GtkWidget *box = gtk_event_box_new();
1676 GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
1677
1678 gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(image->image));
1679
1680 gtk_widget_show(GTK_WIDGET(image->image));
1681 gtk_widget_show(box);
1682
1683 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), box, anchor);
1684 g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gtk_imhtml_image_clicked), image);
1685 }
1686
1687 GtkIMHtmlScalable *gtk_imhtml_hr_new()
1688 {
1689 GtkIMHtmlHr *hr = g_malloc(sizeof(GtkIMHtmlHr));
1690
1691 GTK_IMHTML_SCALABLE(hr)->scale = gtk_imhtml_hr_scale;
1692 GTK_IMHTML_SCALABLE(hr)->add_to = gtk_imhtml_hr_add_to;
1693 GTK_IMHTML_SCALABLE(hr)->free = gtk_imhtml_hr_free;
1694
1695 hr->sep = gtk_hseparator_new();
1696 gtk_widget_set_size_request(hr->sep, 5000, 2);
1697 gtk_widget_show(hr->sep);
1698
1699 return GTK_IMHTML_SCALABLE(hr);
1700 }
1701
1702 void gtk_imhtml_hr_scale(GtkIMHtmlScalable *scale, int width, int height)
1703 {
1704 gtk_widget_set_size_request(((GtkIMHtmlHr *)scale)->sep, width, 2);
1705 }
1706
1707 void gtk_imhtml_hr_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
1708 {
1709 GtkIMHtmlHr *hr = (GtkIMHtmlHr *)scale;
1710 GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
1711
1712 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), hr->sep, anchor);
1713 }
1714
1715 void gtk_imhtml_hr_free(GtkIMHtmlScalable *scale)
1716 {
1717 /* gtk_widget_destroy(((GtkIMHtmlHr *)scale)->sep); */
1718 g_free(scale);
1719 }