comparison src/gtkimhtml.c @ 4735:66c2388046fb

[gaim-migrate @ 5050] This patch is from Ka-Hing Cheung. It readds tooltips and the hand cursor for GtkIMHtml links. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Thu, 13 Mar 2003 06:56:40 +0000
parents 42d53c416bb9
children beffb3109ddb
comparison
equal deleted inserted replaced
4734:ece4045eebff 4735:66c2388046fb
46 #else 46 #else
47 # define N_(String) (String) 47 # define N_(String) (String)
48 # define _(x) (x) 48 # define _(x) (x)
49 #endif 49 #endif
50 50
51 #include <pango/pango-font.h>
52
53 #define TOOLTIP_TIMEOUT 500
54
55 static gint gtk_imhtml_tip (gpointer data);
56
51 57
52 /* POINT_SIZE converts from AIM font sizes to point sizes. It probably should be redone in such a 58 /* POINT_SIZE converts from AIM font sizes to point sizes. It probably should be redone in such a
53 * way that it base the sizes off the default font size rather than using arbitrary font sizes. */ 59 * way that it base the sizes off the default font size rather than using arbitrary font sizes. */
54 #define MAX_FONT_SIZE 7 60 #define MAX_FONT_SIZE 7
55 #define POINT_SIZE(x) (_point_sizes [MIN ((x), MAX_FONT_SIZE) - 1]) 61 #define POINT_SIZE(x) (_point_sizes [MIN ((x), MAX_FONT_SIZE) - 1])
156 g_hash_table_foreach_remove(imhtml->smiley_data, gtk_smiley_tree_destroy_from_hash, NULL); 162 g_hash_table_foreach_remove(imhtml->smiley_data, gtk_smiley_tree_destroy_from_hash, NULL);
157 g_hash_table_destroy(imhtml->smiley_data); 163 g_hash_table_destroy(imhtml->smiley_data);
158 gtk_smiley_tree_destroy(imhtml->default_smilies); 164 gtk_smiley_tree_destroy(imhtml->default_smilies);
159 gdk_cursor_unref(imhtml->hand_cursor); 165 gdk_cursor_unref(imhtml->hand_cursor);
160 gdk_cursor_unref(imhtml->arrow_cursor); 166 gdk_cursor_unref(imhtml->arrow_cursor);
167 if(imhtml->tip_window){
168 gtk_widget_destroy(imhtml->tip_window);
169 }
170 if(imhtml->tip_timer)
171 gtk_timeout_remove(imhtml->tip_timer);
172
161 G_OBJECT_CLASS(parent_class)->finalize (object); 173 G_OBJECT_CLASS(parent_class)->finalize (object);
162 } 174 }
163 175
164 /* Boring GTK stuff */ 176 /* Boring GTK stuff */
165 static void gtk_imhtml_class_init (GtkIMHtmlClass *class) 177 static void gtk_imhtml_class_init (GtkIMHtmlClass *class)
211 imhtml->show_smileys = TRUE; 223 imhtml->show_smileys = TRUE;
212 imhtml->show_comments = TRUE; 224 imhtml->show_comments = TRUE;
213 225
214 imhtml->smiley_data = g_hash_table_new (g_str_hash, g_str_equal); 226 imhtml->smiley_data = g_hash_table_new (g_str_hash, g_str_equal);
215 imhtml->default_smilies = gtk_smiley_tree_new(); 227 imhtml->default_smilies = gtk_smiley_tree_new();
228
229 g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL);
230
231 imhtml->tip = NULL;
232 imhtml->tip_timer = 0;
233 imhtml->tip_window = NULL;
216 } 234 }
217 235
218 GtkWidget *gtk_imhtml_new(void *a, void *b) 236 GtkWidget *gtk_imhtml_new(void *a, void *b)
219 { 237 {
220 return GTK_WIDGET(g_object_new(gtk_imhtml_get_type(), NULL)); 238 return GTK_WIDGET(g_object_new(gtk_imhtml_get_type(), NULL));
291 309
292 menu = gtk_menu_new(); 310 menu = gtk_menu_new();
293 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 311 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
294 event_button->button, event_button->time); 312 event_button->button, event_button->time);
295 313
296 /* URL of link, desensitized */
297 item = gtk_menu_item_new();
298 label = gtk_label_new(url);
299 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
300 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
301 gtk_container_add(GTK_CONTAINER(item), label);
302 gtk_widget_set_sensitive(item, FALSE);
303 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
304
305 /* seperator */
306 item = gtk_menu_item_new();
307 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
308
309 /* buttons and such */ 314 /* buttons and such */
310 img = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU); 315 img = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
311 item = gtk_image_menu_item_new_with_mnemonic(_("_Copy Link Location")); 316 item = gtk_image_menu_item_new_with_mnemonic(_("_Copy Link Location"));
312 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); 317 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
313 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_copy), 318 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_copy),
322 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); 327 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
323 gtk_widget_show_all(menu); 328 gtk_widget_show_all(menu);
324 329
325 return TRUE; 330 return TRUE;
326 } 331 }
327 } else if (event->type == GDK_ENTER_NOTIFY) {
328 /* make a hand cursor and a tooltip timeout -- if GTK worked as it should */
329 } else if (event->type == GDK_LEAVE_NOTIFY) {
330 /* clear timeout and make an arrow cursor again --if GTK worked as it should */
331 } 332 }
332 if(event->type == GDK_BUTTON_PRESS && event_button->button == 3) 333 if(event->type == GDK_BUTTON_PRESS && event_button->button == 3)
333 return TRUE; /* Clicking the right mouse button on a link shouldn't 334 return TRUE; /* Clicking the right mouse button on a link shouldn't
334 be caught by the regular GtkTextView menu */ 335 be caught by the regular GtkTextView menu */
335 else 336 else
336 return FALSE; /* Let clicks go through if we didn't catch anything */ 337 return FALSE; /* Let clicks go through if we didn't catch anything */
338 }
339
340 gboolean gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer data)
341 {
342 GtkTextIter iter;
343 GdkWindow *win = event->window;
344 int x, y;
345 GSList *tags = NULL, *templist = NULL;
346
347 gdk_window_get_pointer(GTK_WIDGET(imhtml)->window, NULL, NULL, NULL);
348
349 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml), GTK_TEXT_WINDOW_WIDGET,
350 event->x, event->y, &x, &y);
351 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y);
352
353 if (GTK_IMHTML(imhtml)->tip_window) {
354 gtk_widget_destroy (GTK_IMHTML(imhtml)->tip_window);
355 GTK_IMHTML(imhtml)->tip_window = NULL;
356 }
357 if (GTK_IMHTML(imhtml)->tip_timer) {
358 gtk_timeout_remove (GTK_IMHTML(imhtml)->tip_timer);
359 GTK_IMHTML(imhtml)->tip_timer = 0;
360 }
361
362 GTK_IMHTML(imhtml)->tip = NULL;
363
364 tags = gtk_text_iter_get_tags(&iter);
365
366 for(templist = tags; !GTK_IMHTML(imhtml)->tip && templist; templist = templist->next){
367 GtkTextTag *tag = templist->data;
368 GTK_IMHTML(imhtml)->tip = g_object_get_data(G_OBJECT(tag), "link_url");
369 }
370
371 if(GTK_IMHTML(imhtml)->tip){
372 gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor);
373 GTK_IMHTML(imhtml)->tip_timer = gtk_timeout_add (TOOLTIP_TIMEOUT,
374 gtk_imhtml_tip, imhtml);
375 }
376 else{
377 gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->arrow_cursor);
378 }
379
380 g_slist_free(tags);
381 return FALSE;
337 } 382 }
338 383
339 /* this isn't used yet 384 /* this isn't used yet
340 static void 385 static void
341 gtk_smiley_tree_remove (GtkSmileyTree *tree, 386 gtk_smiley_tree_remove (GtkSmileyTree *tree,
722 } \ 767 } \
723 if (url) { \ 768 if (url) { \
724 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); \ 769 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); \
725 g_signal_connect(G_OBJECT(texttag), "event", G_CALLBACK(tag_event), strdup(url)); \ 770 g_signal_connect(G_OBJECT(texttag), "event", G_CALLBACK(tag_event), strdup(url)); \
726 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ 771 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
772 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, NULL); \
773 g_object_set_data(G_OBJECT(texttag), "link_url", g_strdup(url)); \
774 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
727 } \ 775 } \
728 wpos = 0; \ 776 wpos = 0; \
729 ws[0] = 0; \ 777 ws[0] = 0; \
730 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ 778 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \
731 if (x == NEW_HR_BIT) { \ 779 if (x == NEW_HR_BIT) { \
1164 void gtk_imhtml_page_up (GtkIMHtml *imhtml) 1212 void gtk_imhtml_page_up (GtkIMHtml *imhtml)
1165 { 1213 {
1166 1214
1167 } 1215 }
1168 void gtk_imhtml_page_down (GtkIMHtml *imhtml){} 1216 void gtk_imhtml_page_down (GtkIMHtml *imhtml){}
1217
1218 static gint
1219 gtk_imhtml_tip_paint (GtkIMHtml *imhtml)
1220 {
1221 PangoLayout *layout;
1222
1223 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
1224
1225 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
1226
1227 gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window,
1228 GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, imhtml->tip_window,
1229 "tooltip", 0, 0, -1, -1);
1230
1231 gtk_paint_layout (imhtml->tip_window->style, imhtml->tip_window->window, GTK_STATE_NORMAL,
1232 FALSE, NULL, imhtml->tip_window, NULL, 4, 4, layout);
1233
1234 g_free(layout);
1235 return FALSE;
1236 }
1237
1238 static gint
1239 gtk_imhtml_tip (gpointer data)
1240 {
1241 GtkIMHtml *imhtml = data;
1242 PangoFontMetrics *font;
1243 PangoLayout *layout;
1244
1245 gint gap, x, y, h, w, scr_w, baseline_skip;
1246
1247 g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
1248
1249 if (!imhtml->tip || !GTK_WIDGET_DRAWABLE (GTK_WIDGET(imhtml))) {
1250 imhtml->tip_timer = 0;
1251 return FALSE;
1252 }
1253
1254 if (imhtml->tip_window){
1255 gtk_widget_destroy (imhtml->tip_window);
1256 imhtml->tip_window = NULL;
1257 }
1258
1259 imhtml->tip_timer = 0;
1260 imhtml->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
1261 gtk_widget_set_app_paintable (imhtml->tip_window, TRUE);
1262 gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
1263 gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
1264 g_signal_connect_swapped (G_OBJECT (imhtml->tip_window), "expose_event",
1265 G_CALLBACK (gtk_imhtml_tip_paint), imhtml);
1266
1267 gtk_widget_ensure_style (imhtml->tip_window);
1268 layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
1269 font = pango_font_get_metrics(pango_context_load_font(pango_layout_get_context(layout),
1270 imhtml->tip_window->style->font_desc),
1271 NULL);
1272
1273
1274 pango_layout_get_pixel_size(layout, &scr_w, NULL);
1275 gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font) +
1276 pango_font_metrics_get_descent(font))/ 4);
1277
1278 if (gap < 2)
1279 gap = 2;
1280 baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font) +
1281 pango_font_metrics_get_descent(font));
1282 w = 8 + scr_w;
1283 h = 8 + baseline_skip;
1284
1285 gdk_window_get_pointer (NULL, &x, &y, NULL);
1286 if (GTK_WIDGET_NO_WINDOW (GTK_WIDGET(imhtml)))
1287 y += GTK_WIDGET(imhtml)->allocation.y;
1288
1289 scr_w = gdk_screen_width();
1290
1291 x -= ((w >> 1) + 4);
1292
1293 if ((x + w) > scr_w)
1294 x -= (x + w) - scr_w;
1295 else if (x < 0)
1296 x = 0;
1297
1298 y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font) +
1299 pango_font_metrics_get_descent(font));
1300
1301 gtk_widget_set_size_request (imhtml->tip_window, w, h);
1302 gtk_widget_show (imhtml->tip_window);
1303 gtk_window_move (GTK_WINDOW(imhtml->tip_window), x, y);
1304
1305 pango_font_metrics_unref(font);
1306 g_free(layout);
1307
1308 return FALSE;
1309 }