Mercurial > pidgin
comparison src/buddy.c @ 4724:b9e7ccf21f9c
[gaim-migrate @ 5037]
I stayed up all night and readded tooltips. It feels great.
GTK 2 provides no easy way to add a tooltip to a row of a GtkTreeView so
I had to do it all by hand. In doing so, I took some liberties and did
some stuff most tooltips can't do... my tooltips have Markup and Pixbufs
in them =-).
Tomorrow, KingAnt gets back. I'll let him readd the AIM-specific tooltip
stuff. Capabilities and logged on time used to live in the buddy struct, but
were removed for being too aim-centric. KingAnt will put them back, I'm sure.
committer: Tailor Script <tailor@pidgin.im>
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Wed, 12 Mar 2003 11:37:31 +0000 |
parents | 534eaa4ead94 |
children | fd44726a527f |
comparison
equal
deleted
inserted
replaced
4723:39068784aa08 | 4724:b9e7ccf21f9c |
---|---|
58 | 58 |
59 /* Docklet nonsense */ | 59 /* Docklet nonsense */ |
60 static gboolean gaim_gtk_blist_obscured = FALSE; | 60 static gboolean gaim_gtk_blist_obscured = FALSE; |
61 | 61 |
62 static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node); | 62 static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node); |
63 static char *gaim_get_tooltip_text(struct buddy *b); | |
64 static GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b, GaimStatusIconSize size); | |
63 | 65 |
64 /*************************************************** | 66 /*************************************************** |
65 * Callbacks * | 67 * Callbacks * |
66 ***************************************************/ | 68 ***************************************************/ |
67 | 69 |
334 gtk_tree_path_free(path); | 336 gtk_tree_path_free(path); |
335 } | 337 } |
336 } | 338 } |
337 } | 339 } |
338 | 340 |
341 static void gaim_gtk_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, struct buddy *b) | |
342 { | |
343 int x,y,scr_w,scr_h, w, h; | |
344 GtkStyle *style; | |
345 GdkPixbuf *pixbuf = gaim_gtk_blist_get_status_icon(b, GAIM_STATUS_ICON_LARGE); | |
346 PangoLayout *layout; | |
347 char *tooltiptext = gaim_get_tooltip_text(b); | |
348 | |
349 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); | |
350 pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); | |
351 style = gtkblist->tipwindow->style; | |
352 | |
353 gtk_paint_flat_box (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, | |
354 NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); | |
355 | |
356 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, pixbuf, | |
357 0, 0, 4, 4, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); | |
358 | |
359 gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, TRUE, | |
360 NULL, gtkblist->tipwindow, "tooltip", 38, 4, layout); | |
361 | |
362 g_object_unref (pixbuf); | |
363 g_object_unref (layout); | |
364 g_free(tooltiptext); | |
365 return; | |
366 } | |
367 | |
368 static gboolean gaim_gtk_blist_tooltip_timeout(GtkWidget *tv) | |
369 { | |
370 GtkTreePath *path; | |
371 GtkTreeIter iter; | |
372 GaimBlistNode *node; | |
373 char *tooltiptext; | |
374 GValue val = {0}; | |
375 | |
376 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->rect.x, gtkblist->rect.y, &path, NULL, NULL, NULL)) | |
377 return FALSE; | |
378 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
379 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
380 node = g_value_get_pointer(&val); | |
381 | |
382 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
383 int scr_w,scr_h, w, h, x, y; | |
384 PangoLayout *layout; | |
385 struct buddy *buddy = (struct buddy*)node; | |
386 char *tooltiptext = gaim_get_tooltip_text(buddy); | |
387 gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); | |
388 gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); | |
389 gtk_window_set_policy(GTK_WINDOW(gtkblist->tipwindow), FALSE, FALSE, TRUE); | |
390 gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); | |
391 g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", | |
392 G_CALLBACK(gaim_gtk_blist_paint_tip), buddy); | |
393 gtk_widget_ensure_style (gtkblist->tipwindow); | |
394 | |
395 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); | |
396 pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); | |
397 scr_w = gdk_screen_width(); | |
398 scr_h = gdk_screen_height(); | |
399 pango_layout_get_size (layout, &w, &h); | |
400 w = PANGO_PIXELS(w) + 8; | |
401 h = PANGO_PIXELS(h) + 8; | |
402 | |
403 /* 38 is the size of a large status icon plus 4 pixels padding on each side. | |
404 I should #define this or something */ | |
405 w = w + 38; | |
406 h = MAX(h, 38); | |
407 | |
408 gdk_window_get_pointer(NULL, &x, &y, NULL); | |
409 if (GTK_WIDGET_NO_WINDOW(gtkblist->window)) | |
410 y+=gtkblist->window->allocation.y; | |
411 | |
412 x -= ((w >> 1) + 4); | |
413 | |
414 if ((x + w) > scr_w) | |
415 x -= (x + w) - scr_w; | |
416 else if (x < 0) | |
417 x = 0; | |
418 | |
419 if ((y + h + 4) > scr_h) | |
420 y = y - h; | |
421 else | |
422 y = y + 6; | |
423 g_object_unref (layout); | |
424 g_free(tooltiptext); | |
425 gtk_widget_set_size_request(gtkblist->tipwindow, w, h); | |
426 gtk_widget_set_uposition(gtkblist->tipwindow, x, y); | |
427 gtk_widget_show(gtkblist->tipwindow); | |
428 } | |
429 | |
430 gtk_tree_path_free(path); | |
431 return FALSE; | |
432 } | |
433 | |
434 static void gaim_gtk_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) | |
435 { | |
436 GtkTreePath *path; | |
437 | |
438 if (gtkblist->timeout) { | |
439 if ((event->y > gtkblist->rect.y) && ((event->y - gtkblist->rect.height) < gtkblist->rect.y)) | |
440 return; | |
441 /* We've left the cell. Remove the timeout and create a new one below */ | |
442 if (gtkblist->tipwindow) { | |
443 gtk_widget_destroy(gtkblist->tipwindow); | |
444 gtkblist->tipwindow = NULL; | |
445 } | |
446 | |
447 g_source_remove(gtkblist->timeout); | |
448 } | |
449 | |
450 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); | |
451 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->rect); | |
452 if (path) | |
453 gtk_tree_path_free(path); | |
454 gtkblist->timeout = g_timeout_add(500, (GSourceFunc)gaim_gtk_blist_tooltip_timeout, tv); | |
455 } | |
456 | |
457 static void gaim_gtk_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) | |
458 { | |
459 if (gtkblist->timeout == 0) | |
460 return; | |
461 if (gtkblist->tipwindow) { | |
462 gtk_widget_destroy(gtkblist->tipwindow); | |
463 gtkblist->tipwindow = NULL; | |
464 } | |
465 g_source_remove(gtkblist->timeout); | |
466 gtkblist->timeout = 0; | |
467 } | |
468 | |
339 /*************************************************** | 469 /*************************************************** |
340 * Crap * | 470 * Crap * |
341 ***************************************************/ | 471 ***************************************************/ |
342 static GtkItemFactoryEntry blist_menu[] = | 472 static GtkItemFactoryEntry blist_menu[] = |
343 { | 473 { |
383 | 513 |
384 /********************************************************* | 514 /********************************************************* |
385 * Private Utility functions * | 515 * Private Utility functions * |
386 *********************************************************/ | 516 *********************************************************/ |
387 | 517 |
388 static GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b) | 518 static char *gaim_get_tooltip_text(struct buddy *b) |
519 { | |
520 char *text = NULL; | |
521 struct prpl* prpl = find_prpl(b->account->protocol); | |
522 char *statustext = NULL; | |
523 char *tooltiptext = NULL; | |
524 char *warning = NULL, *idletime = NULL; | |
525 | |
526 if (prpl->tooltip_text) { | |
527 char *tmp = prpl->tooltip_text(b); | |
528 if (tmp) { | |
529 statustext = g_markup_escape_text(tmp, strlen(tmp)); | |
530 g_free(tmp); | |
531 } | |
532 } | |
533 | |
534 if (b->idle) { | |
535 int ihrs, imin; | |
536 time_t t; | |
537 time(&t); | |
538 ihrs = (t - b->idle) / 3600; | |
539 imin = ((t - b->idle) / 60) % 60; | |
540 if (ihrs) | |
541 idletime = g_strdup_printf(_("<b>Idle</b> %dh%02dm"), ihrs, imin); | |
542 else | |
543 idletime = g_strdup_printf(_("<b>Idle</b> %dm"), imin); | |
544 } | |
545 | |
546 if (b->evil > 0) | |
547 warning = g_strdup_printf(_("<b>Warned</b> %d%%"), b->evil); | |
548 | |
549 text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>" | |
550 "%s %s %s" /* Alias */ | |
551 "%s %s %s" /* Nickname */ | |
552 "%s %s" /* Idle */ | |
553 "%s %s" /* Warning */ | |
554 "%s %s", /* Status */ | |
555 b->name, | |
556 b->alias && b->alias[0] ? "\n" : "", b->alias && b->alias[0] ? _("<b>Alias</b> ") : "", b->alias ? b->alias : "", | |
557 b->server_alias ? "\n" : "", b->server_alias ? _("<b>Nickname</b> ") : "", b->server_alias ? b->server_alias : "", | |
558 b->idle ? "\n" : "", b->idle ? idletime : "", | |
559 b->evil ? "\n" : "", b->evil ? warning : "", | |
560 statustext ? "\n" : "", statustext ? statustext : ""); | |
561 return text; | |
562 | |
563 } | |
564 | |
565 static GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b, GaimStatusIconSize size) | |
389 { | 566 { |
390 GdkPixbuf *status = NULL; | 567 GdkPixbuf *status = NULL; |
391 GdkPixbuf *scale = NULL; | 568 GdkPixbuf *scale = NULL; |
392 GdkPixbuf *emblem = NULL; | 569 GdkPixbuf *emblem = NULL; |
393 gchar *filename = NULL; | 570 gchar *filename = NULL; |
401 if (prpl->list_icon) | 578 if (prpl->list_icon) |
402 protoname = prpl->list_icon(b->account, b); | 579 protoname = prpl->list_icon(b->account, b); |
403 if (prpl->list_emblems) | 580 if (prpl->list_emblems) |
404 prpl->list_emblems(b, &se, &sw, &nw, &ne); | 581 prpl->list_emblems(b, &se, &sw, &nw, &ne); |
405 | 582 |
406 if (!(blist_options & OPT_BLIST_SHOW_ICONS)) { | 583 if (size == GAIM_STATUS_ICON_SMALL) { |
407 scalesize = 15; | 584 scalesize = 15; |
408 sw = nw = ne = NULL; /* So that only the se icon will composite */ | 585 sw = nw = ne = NULL; /* So that only the se icon will composite */ |
409 } | 586 } |
410 | 587 |
411 | 588 |
447 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | 624 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); |
448 g_free(image); | 625 g_free(image); |
449 emblem = gdk_pixbuf_new_from_file(filename,NULL); | 626 emblem = gdk_pixbuf_new_from_file(filename,NULL); |
450 g_free(filename); | 627 g_free(filename); |
451 if (emblem) { | 628 if (emblem) { |
452 if (blist_options & OPT_BLIST_SHOW_ICONS) | 629 if (size == GAIM_STATUS_ICON_LARGE) |
453 gdk_pixbuf_composite (emblem, | 630 gdk_pixbuf_composite (emblem, |
454 scale, 15, 15, | 631 scale, 15, 15, |
455 15, 15, | 632 15, 15, |
456 15, 15, | 633 15, 15, |
457 1, 1, | 634 1, 1, |
548 struct prpl* prpl = find_prpl(b->account->protocol); | 725 struct prpl* prpl = find_prpl(b->account->protocol); |
549 | 726 |
550 /* XXX Clean up this crap */ | 727 /* XXX Clean up this crap */ |
551 | 728 |
552 int ihrs, imin; | 729 int ihrs, imin; |
553 char *idletime = NULL, *warning = NULL; | 730 char *idletime = NULL, *warning = NULL, *statustext = NULL; |
554 const char *statustext = NULL; | |
555 time_t t; | 731 time_t t; |
556 | 732 |
557 if (!(blist_options & OPT_BLIST_SHOW_ICONS)) { | 733 if (!(blist_options & OPT_BLIST_SHOW_ICONS)) { |
558 if (b->idle > 0 && blist_options & OPT_BLIST_GREY_IDLERS) { | 734 if (b->idle > 0 && blist_options & OPT_BLIST_GREY_IDLERS) { |
559 text = g_strdup_printf("<span color='dim grey'>%s</span>", | 735 text = g_strdup_printf("<span color='dim grey'>%s</span>", |
568 time(&t); | 744 time(&t); |
569 ihrs = (t - b->idle) / 3600; | 745 ihrs = (t - b->idle) / 3600; |
570 imin = ((t - b->idle) / 60) % 60; | 746 imin = ((t - b->idle) / 60) % 60; |
571 | 747 |
572 if (prpl->status_text) { | 748 if (prpl->status_text) { |
573 char *tmp = prpl->status_text(b); | 749 const char *tmp = prpl->status_text(b); |
574 if (tmp) | 750 if (tmp) |
575 statustext = g_markup_escape_text(tmp, strlen(tmp)); | 751 statustext = g_markup_escape_text(tmp, strlen(tmp)); |
576 } | 752 } |
577 | 753 |
578 if (b->idle) { | 754 if (b->idle) { |
679 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2, | 855 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2, |
680 GDK_ACTION_COPY | GDK_ACTION_MOVE); | 856 GDK_ACTION_COPY | GDK_ACTION_MOVE); |
681 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL); | 857 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL); |
682 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL); | 858 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL); |
683 | 859 |
860 /* Tooltips */ | |
861 g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(gaim_gtk_blist_motion_cb), NULL); | |
862 g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(gaim_gtk_blist_leave_cb), NULL); | |
684 | 863 |
685 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); | 864 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); |
686 | 865 |
687 rend = gtk_cell_renderer_pixbuf_new(); | 866 rend = gtk_cell_renderer_pixbuf_new(); |
688 column = gtk_tree_view_column_new_with_attributes("Status", rend, "pixbuf", STATUS_ICON_COLUMN, NULL); | 867 column = gtk_tree_view_column_new_with_attributes("Status", rend, "pixbuf", STATUS_ICON_COLUMN, NULL); |
901 if (GAIM_BLIST_NODE_IS_BUDDY(node) && ((struct buddy*)node)->present) { | 1080 if (GAIM_BLIST_NODE_IS_BUDDY(node) && ((struct buddy*)node)->present) { |
902 GdkPixbuf *status, *avatar; | 1081 GdkPixbuf *status, *avatar; |
903 char *mark; | 1082 char *mark; |
904 char *warning = NULL, *idle = NULL; | 1083 char *warning = NULL, *idle = NULL; |
905 | 1084 |
906 status = gaim_gtk_blist_get_status_icon((struct buddy*)node); | 1085 status = gaim_gtk_blist_get_status_icon((struct buddy*)node, |
1086 blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL); | |
907 avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node); | 1087 avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node); |
908 mark = gaim_gtk_blist_get_name_markup((struct buddy*)node); | 1088 mark = gaim_gtk_blist_get_name_markup((struct buddy*)node); |
909 | 1089 |
910 if ((((struct buddy*)node)->idle > 0) && | 1090 if ((((struct buddy*)node)->idle > 0) && |
911 (!(blist_options & OPT_BLIST_SHOW_ICONS) && (blist_options & OPT_BLIST_SHOW_IDLETIME))) { | 1091 (!(blist_options & OPT_BLIST_SHOW_ICONS) && (blist_options & OPT_BLIST_SHOW_IDLETIME))) { |