comparison pidgin/gtkconv.c @ 32117:585d6f844f79

propagate from branch 'im.pidgin.pidgin' (head 37329f033a30f4f4f5048f9fdc86df4400e3ada5) to branch 'im.pidgin.soc.2009.webkitmessageview' (head 7be18cb8139c12d8080e3e0603264c2a623a15d3)
author Paul Aurich <paul@darkrain42.org>
date Mon, 09 Nov 2009 01:42:24 +0000
parents e7bb163434c7 7c346c5a05d4
children 041d5144730f
comparison
equal deleted inserted replaced
28528:231af7ce1b49 32117:585d6f844f79
67 #include "gtkpounce.h" 67 #include "gtkpounce.h"
68 #include "gtkprefs.h" 68 #include "gtkprefs.h"
69 #include "gtkprivacy.h" 69 #include "gtkprivacy.h"
70 #include "gtkthemes.h" 70 #include "gtkthemes.h"
71 #include "gtkutils.h" 71 #include "gtkutils.h"
72 #include "gtkwebview.h"
72 #include "pidginstock.h" 73 #include "pidginstock.h"
73 #include "pidgintooltip.h" 74 #include "pidgintooltip.h"
74 75
75 #include "gtknickcolors.h" 76 #include "gtknickcolors.h"
76 77
162 static void update_typing_message(PidginConversation *gtkconv, const char *message); 163 static void update_typing_message(PidginConversation *gtkconv, const char *message);
163 static const char *item_factory_translate_func (const char *path, gpointer func_data); 164 static const char *item_factory_translate_func (const char *path, gpointer func_data);
164 gboolean pidgin_conv_has_focus(PurpleConversation *conv); 165 gboolean pidgin_conv_has_focus(PurpleConversation *conv);
165 static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background); 166 static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background);
166 static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast); 167 static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast);
167 static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, PurpleMessageFlags flag, gboolean create);
168 static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields); 168 static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields);
169 static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win); 169 static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win);
170 static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv); 170 static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv);
171 static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv); 171 static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv);
172 static void hide_conv(PidginConversation *gtkconv, gboolean closetimer); 172 static void hide_conv(PidginConversation *gtkconv, gboolean closetimer);
176 static gboolean pidgin_conv_xy_to_right_infopane(PidginWindow *win, int x, int y); 176 static gboolean pidgin_conv_xy_to_right_infopane(PidginWindow *win, int x, int y);
177 177
178 static const GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name) 178 static const GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name)
179 { 179 {
180 static GdkColor col; 180 static GdkColor col;
181 GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml); 181 GtkStyle *style = gtk_widget_get_style(gtkconv->webview);
182 float scale; 182 float scale;
183 183
184 col = nick_colors[g_str_hash(name) % nbr_nick_colors]; 184 col = nick_colors[g_str_hash(name) % nbr_nick_colors];
185 scale = ((1-(LUMINANCE(style->base[GTK_STATE_NORMAL]) / LUMINANCE(style->white))) * 185 scale = ((1-(LUMINANCE(style->base[GTK_STATE_NORMAL]) / LUMINANCE(style->white))) *
186 (LUMINANCE(style->white)/MAX(MAX(col.red, col.blue), col.green))); 186 (LUMINANCE(style->white)/MAX(MAX(col.red, col.blue), col.green)));
371 PidginConversation *gtkconv = NULL; 371 PidginConversation *gtkconv = NULL;
372 GList *iter; 372 GList *iter;
373 373
374 gtkconv = PIDGIN_CONVERSATION(conv); 374 gtkconv = PIDGIN_CONVERSATION(conv);
375 375
376 gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml)); 376 webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (gtkconv->webview), "", "");
377 for (iter = gtkconv->convs; iter; iter = iter->next) 377 for (iter = gtkconv->convs; iter; iter = iter->next)
378 purple_conversation_clear_message_history(iter->data); 378 purple_conversation_clear_message_history(iter->data);
379 } 379 }
380 380
381 static PurpleCmdRet 381 static PurpleCmdRet
985 } 985 }
986 986
987 static void 987 static void
988 savelog_writefile_cb(void *user_data, const char *filename) 988 savelog_writefile_cb(void *user_data, const char *filename)
989 { 989 {
990 PurpleConversation *conv = (PurpleConversation *)user_data; 990 /* TODO: I don't know how to support this using webkit yet. */
991 FILE *fp;
992 const char *name;
993 char **lines;
994 gchar *text;
995
996 if ((fp = g_fopen(filename, "w+")) == NULL) {
997 purple_notify_error(PIDGIN_CONVERSATION(conv), NULL, _("Unable to open file."), NULL);
998 return;
999 }
1000
1001 name = purple_conversation_get_name(conv);
1002 fprintf(fp, "<html>\n<head>\n");
1003 fprintf(fp, "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n");
1004 fprintf(fp, "<title>%s</title>\n</head>\n<body>\n", name);
1005 fprintf(fp, _("<h1>Conversation with %s</h1>\n"), name);
1006
1007 lines = gtk_imhtml_get_markup_lines(
1008 GTK_IMHTML(PIDGIN_CONVERSATION(conv)->imhtml));
1009 text = g_strjoinv("<br>\n", lines);
1010 fprintf(fp, "%s", text);
1011 g_free(text);
1012 g_strfreev(lines);
1013
1014 fprintf(fp, "\n</body>\n</html>\n");
1015 fclose(fp);
1016 } 991 }
1017 992
1018 /* 993 /*
1019 * It would be kinda cool if this gave the option of saving a 994 * It would be kinda cool if this gave the option of saving a
1020 * plaintext v. HTML file. 995 * plaintext v. HTML file.
1140 { 1115 {
1141 PidginConversation *gtkconv = iter->data; 1116 PidginConversation *gtkconv = iter->data;
1142 1117
1143 if (gtkconv != gtk_active_conv) 1118 if (gtkconv != gtk_active_conv)
1144 { 1119 {
1145 gtk_imhtml_search_clear(GTK_IMHTML(gtkconv->imhtml)); 1120 webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (gtkconv->webview));
1146 } 1121 }
1147 else 1122 else
1148 { 1123 {
1149 gtk_imhtml_search_find(GTK_IMHTML(gtk_active_conv->imhtml), 1124 webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (gtkconv->webview),
1150 gtk_entry_get_text(GTK_ENTRY(s->entry))); 1125 gtk_entry_get_text (GTK_ENTRY(s->entry)), TRUE, 0);
1126 webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (gtkconv->webview),
1127 true);
1128 webkit_web_view_search_text (WEBKIT_WEB_VIEW (gtkconv->webview),
1129 gtk_entry_get_text (GTK_ENTRY(s->entry)), FALSE, TRUE, FALSE);
1151 } 1130 }
1152 } 1131 }
1153 break; 1132 break;
1154 1133
1155 case GTK_RESPONSE_DELETE_EVENT: 1134 case GTK_RESPONSE_DELETE_EVENT:
1156 case GTK_RESPONSE_CLOSE: 1135 case GTK_RESPONSE_CLOSE:
1157 /* clear the keyword highlighting in all the conversation windows */ 1136 /* clear the keyword highlighting in all the conversation windows */
1158 for (iter = pidgin_conv_window_get_gtkconvs(s->gtkwin); iter; iter=iter->next) 1137 for (iter = pidgin_conv_window_get_gtkconvs(s->gtkwin); iter; iter=iter->next)
1159 { 1138 {
1160 PidginConversation *gconv = iter->data; 1139 PidginConversation *gconv = iter->data;
1161 gtk_imhtml_search_clear(GTK_IMHTML(gconv->imhtml)); 1140 webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW(gconv->webview));
1162 } 1141 }
1163 1142
1164 gtk_widget_destroy(s->gtkwin->dialogs.search); 1143 gtk_widget_destroy(s->gtkwin->dialogs.search);
1165 s->gtkwin->dialogs.search = NULL; 1144 s->gtkwin->dialogs.search = NULL;
1166 g_free(s); 1145 g_free(s);
1659 purple_blist_request_add_buddy(account, name, NULL, NULL); 1638 purple_blist_request_add_buddy(account, name, NULL, NULL);
1660 1639
1661 gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry); 1640 gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry);
1662 } 1641 }
1663 1642
1664 static GtkTextMark *
1665 get_mark_for_user(PidginConversation *gtkconv, const char *who)
1666 {
1667 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml));
1668 char *tmp = g_strconcat("user:", who, NULL);
1669 GtkTextMark *mark = gtk_text_buffer_get_mark(buf, tmp);
1670
1671 g_free(tmp);
1672 return mark;
1673 }
1674
1675 static void 1643 static void
1676 menu_last_said_cb(GtkWidget *w, PidginConversation *gtkconv) 1644 menu_last_said_cb(GtkWidget *w, PidginConversation *gtkconv)
1677 { 1645 {
1678 GtkTextMark *mark; 1646 /* I don't know what this is! */
1679 const char *who; 1647 return;
1680
1681 who = g_object_get_data(G_OBJECT(w), "user_data");
1682 mark = get_mark_for_user(gtkconv, who);
1683
1684 if (mark != NULL)
1685 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0);
1686 else
1687 g_return_if_reached();
1688 } 1648 }
1689 1649
1690 static GtkWidget * 1650 static GtkWidget *
1691 create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc) 1651 create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc)
1692 { 1652 {
1797 } 1757 }
1798 1758
1799 button = pidgin_new_item_from_stock(menu, _("Last Said"), GTK_STOCK_INDEX, 1759 button = pidgin_new_item_from_stock(menu, _("Last Said"), GTK_STOCK_INDEX,
1800 G_CALLBACK(menu_last_said_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL); 1760 G_CALLBACK(menu_last_said_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL);
1801 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); 1761 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
1802 if (!get_mark_for_user(PIDGIN_CONVERSATION(conv), who))
1803 gtk_widget_set_sensitive(button, FALSE);
1804 1762
1805 if (buddy != NULL) 1763 if (buddy != NULL)
1806 { 1764 {
1807 if (purple_account_is_connected(conv->account)) 1765 if (purple_account_is_connected(conv->account))
1808 pidgin_append_blist_node_proto_menu(menu, conv->account->gc, 1766 pidgin_append_blist_node_proto_menu(menu, conv->account->gc,
1888 1846
1889 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { 1847 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
1890 chat_do_im(gtkconv, who); 1848 chat_do_im(gtkconv, who);
1891 } else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) { 1849 } else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) {
1892 /* Move to user's anchor */ 1850 /* Move to user's anchor */
1893 GtkTextMark *mark = get_mark_for_user(gtkconv, who); 1851 //GtkTextMark *mark = get_mark_for_user(gtkconv, who);
1894 1852
1895 if(mark != NULL) 1853 //if(mark != NULL)
1896 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0); 1854 // gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0);
1897 } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { 1855 } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
1898 GtkWidget *menu = create_chat_menu (conv, who, gc); 1856 GtkWidget *menu = create_chat_menu (conv, who, gc);
1899 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 1857 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1900 event->button, event->time); 1858 event->button, event->time);
1901 } 1859 }
1967 GtkWidget *next = NULL; 1925 GtkWidget *next = NULL;
1968 struct { 1926 struct {
1969 GtkWidget *from; 1927 GtkWidget *from;
1970 GtkWidget *to; 1928 GtkWidget *to;
1971 } transitions[] = { 1929 } transitions[] = {
1972 {gtkconv->entry, gtkconv->imhtml}, 1930 {gtkconv->entry, gtkconv->webview},
1973 {gtkconv->imhtml, chat ? gtkconv->u.chat->list : gtkconv->entry}, 1931 {gtkconv->webview, chat ? gtkconv->u.chat->list : gtkconv->entry},
1974 {chat ? gtkconv->u.chat->list : NULL, gtkconv->entry}, 1932 {chat ? gtkconv->u.chat->list : NULL, gtkconv->entry},
1975 {NULL, NULL} 1933 {NULL, NULL}
1976 }, *ptr; 1934 }, *ptr;
1977 1935
1978 for (ptr = transitions; !next && ptr->from; ptr++) { 1936 for (ptr = transitions; !next && ptr->from; ptr++) {
2224 break; 2182 break;
2225 return tab_complete(conv); 2183 return tab_complete(conv);
2226 break; 2184 break;
2227 2185
2228 case GDK_Page_Up: 2186 case GDK_Page_Up:
2229 gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml)); 2187 //gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml));
2230 return TRUE; 2188 return TRUE;
2231 break; 2189 break;
2232 2190
2233 case GDK_Page_Down: 2191 case GDK_Page_Down:
2234 gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml)); 2192 //gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml));
2235 return TRUE; 2193 return TRUE;
2236 break; 2194 break;
2237 2195
2238 } 2196 }
2239 } 2197 }
2332 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(gtkconv->win->menu.logging))); 2290 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(gtkconv->win->menu.logging)));
2333 2291
2334 entry = GTK_IMHTML(gtkconv->entry); 2292 entry = GTK_IMHTML(gtkconv->entry);
2335 protocol_name = purple_account_get_protocol_name(conv->account); 2293 protocol_name = purple_account_get_protocol_name(conv->account);
2336 gtk_imhtml_set_protocol_name(entry, protocol_name); 2294 gtk_imhtml_set_protocol_name(entry, protocol_name);
2337 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol_name); 2295 //gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol_name);
2338 2296
2339 if (!(conv->features & PURPLE_CONNECTION_HTML)) 2297 if (!(conv->features & PURPLE_CONNECTION_HTML))
2340 gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry)); 2298 gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry));
2341 else if (conv->features & PURPLE_CONNECTION_FORMATTING_WBFO && 2299 else if (conv->features & PURPLE_CONNECTION_FORMATTING_WBFO &&
2342 !(old_conv->features & PURPLE_CONNECTION_FORMATTING_WBFO)) 2300 !(old_conv->features & PURPLE_CONNECTION_FORMATTING_WBFO))
3276 conv = gtkconv->active_conv; 3234 conv = gtkconv->active_conv;
3277 3235
3278 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { 3236 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
3279 chat = purple_blist_find_chat(conv->account, conv->name); 3237 chat = purple_blist_find_chat(conv->account, conv->name);
3280 3238
3281 if ((chat == NULL) && (gtkconv->imhtml != NULL)) { 3239 if ((chat == NULL) && (gtkconv->webview != NULL)) {
3282 chat = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat"); 3240 chat = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat");
3283 } 3241 }
3284 3242
3285 if ((chat == NULL) && (gtkconv->imhtml != NULL)) { 3243 if ((chat == NULL) && (gtkconv->webview != NULL)) {
3286 GHashTable *components; 3244 GHashTable *components;
3287 PurpleAccount *account = purple_conversation_get_account(conv); 3245 PurpleAccount *account = purple_conversation_get_account(conv);
3288 PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account)); 3246 PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
3289 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); 3247 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
3290 if (purple_account_get_connection(account) != NULL && 3248 if (purple_account_get_connection(account) != NULL &&
3298 g_strdup(purple_conversation_get_name(conv))); 3256 g_strdup(purple_conversation_get_name(conv)));
3299 } 3257 }
3300 chat = purple_chat_new(conv->account, NULL, components); 3258 chat = purple_chat_new(conv->account, NULL, components);
3301 purple_blist_node_set_flags((PurpleBlistNode *)chat, 3259 purple_blist_node_set_flags((PurpleBlistNode *)chat,
3302 PURPLE_BLIST_NODE_FLAG_NO_SAVE); 3260 PURPLE_BLIST_NODE_FLAG_NO_SAVE);
3303 g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_chat", 3261 g_object_set_data_full(G_OBJECT(gtkconv->webview), "transient_chat",
3304 chat, (GDestroyNotify)purple_blist_remove_chat); 3262 chat, (GDestroyNotify)purple_blist_remove_chat);
3305 } 3263 }
3306 } else { 3264 } else {
3307 if (!purple_account_is_connected(conv->account)) 3265 if (!purple_account_is_connected(conv->account))
3308 return FALSE; 3266 return FALSE;
3310 buddy = purple_find_buddy(conv->account, conv->name); 3268 buddy = purple_find_buddy(conv->account, conv->name);
3311 3269
3312 /* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle 3270 /* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle
3313 * removing "isolated" buddy nodes well */ 3271 * removing "isolated" buddy nodes well */
3314 if (purple_version_check(2, 0, 2) == NULL) { 3272 if (purple_version_check(2, 0, 2) == NULL) {
3315 if ((buddy == NULL) && (gtkconv->imhtml != NULL)) { 3273 if ((buddy == NULL) && (gtkconv->webview != NULL)) {
3316 buddy = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy"); 3274 buddy = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy");
3317 } 3275 }
3318 3276
3319 if ((buddy == NULL) && (gtkconv->imhtml != NULL)) { 3277 if ((buddy == NULL) && (gtkconv->webview != NULL)) {
3320 buddy = purple_buddy_new(conv->account, conv->name, NULL); 3278 buddy = purple_buddy_new(conv->account, conv->name, NULL);
3321 purple_blist_node_set_flags((PurpleBlistNode *)buddy, 3279 purple_blist_node_set_flags((PurpleBlistNode *)buddy,
3322 PURPLE_BLIST_NODE_FLAG_NO_SAVE); 3280 PURPLE_BLIST_NODE_FLAG_NO_SAVE);
3323 g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_buddy", 3281 g_object_set_data_full(G_OBJECT(gtkconv->webview), "transient_buddy",
3324 buddy, (GDestroyNotify)purple_buddy_destroy); 3282 buddy, (GDestroyNotify)purple_buddy_destroy);
3325 } 3283 }
3326 } 3284 }
3327 } 3285 }
3328 3286
3726 #endif 3684 #endif
3727 3685
3728 static void 3686 static void
3729 update_typing_message(PidginConversation *gtkconv, const char *message) 3687 update_typing_message(PidginConversation *gtkconv, const char *message)
3730 { 3688 {
3731 GtkTextBuffer *buffer; 3689 /* this is not handled at all */
3732 GtkTextMark *stmark, *enmark;
3733
3734 if (g_object_get_data(G_OBJECT(gtkconv->imhtml), "disable-typing-notification"))
3735 return;
3736
3737 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml));
3738 stmark = gtk_text_buffer_get_mark(buffer, "typing-notification-start");
3739 enmark = gtk_text_buffer_get_mark(buffer, "typing-notification-end");
3740 if (stmark && enmark) {
3741 GtkTextIter start, end;
3742 gtk_text_buffer_get_iter_at_mark(buffer, &start, stmark);
3743 gtk_text_buffer_get_iter_at_mark(buffer, &end, enmark);
3744 gtk_text_buffer_delete_mark(buffer, stmark);
3745 gtk_text_buffer_delete_mark(buffer, enmark);
3746 gtk_text_buffer_delete(buffer, &start, &end);
3747 } else if (message && *message == '\n' && message[1] == ' ' && message[2] == '\0')
3748 message = NULL;
3749
3750 #ifdef RESERVE_LINE
3751 if (!message)
3752 message = "\n "; /* The blank space is required to avoid a GTK+/Pango bug */
3753 #endif
3754
3755 if (message) {
3756 GtkTextIter iter;
3757 gtk_text_buffer_get_end_iter(buffer, &iter);
3758 gtk_text_buffer_create_mark(buffer, "typing-notification-start", &iter, TRUE);
3759 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, message, -1, "TYPING-NOTIFICATION", NULL);
3760 gtk_text_buffer_get_end_iter(buffer, &iter);
3761 gtk_text_buffer_create_mark(buffer, "typing-notification-end", &iter, TRUE);
3762 }
3763 } 3690 }
3764 3691
3765 static void 3692 static void
3766 update_typing_icon(PidginConversation *gtkconv) 3693 update_typing_icon(PidginConversation *gtkconv)
3767 { 3694 {
4074 GtkTreeIter iter; 4001 GtkTreeIter iter;
4075 gboolean is_me = FALSE; 4002 gboolean is_me = FALSE;
4076 gboolean is_buddy; 4003 gboolean is_buddy;
4077 gchar *tmp, *alias_key, *name, *alias; 4004 gchar *tmp, *alias_key, *name, *alias;
4078 int flags; 4005 int flags;
4079 GdkColor *color = NULL;
4080 4006
4081 alias = cb->alias; 4007 alias = cb->alias;
4082 name = cb->name; 4008 name = cb->name;
4083 flags = GPOINTER_TO_INT(cb->flags); 4009 flags = GPOINTER_TO_INT(cb->flags);
4084 4010
4100 is_buddy = cb->buddy; 4026 is_buddy = cb->buddy;
4101 4027
4102 tmp = g_utf8_casefold(alias, -1); 4028 tmp = g_utf8_casefold(alias, -1);
4103 alias_key = g_utf8_collate_key(tmp, -1); 4029 alias_key = g_utf8_collate_key(tmp, -1);
4104 g_free(tmp); 4030 g_free(tmp);
4105
4106 if (is_me) {
4107 GtkTextTag *tag = gtk_text_tag_table_lookup(
4108 gtk_text_buffer_get_tag_table(GTK_IMHTML(gtkconv->imhtml)->text_buffer),
4109 "send-name");
4110 g_object_get(tag, "foreground-gdk", &color, NULL);
4111 } else {
4112 GtkTextTag *tag;
4113 if ((tag = get_buddy_tag(conv, name, 0, FALSE)))
4114 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL);
4115 if ((tag = get_buddy_tag(conv, name, PURPLE_MESSAGE_NICK, FALSE)))
4116 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL);
4117 color = (GdkColor*)get_nick_color(gtkconv, name);
4118 }
4119 4031
4120 #if GTK_CHECK_VERSION(2,6,0) 4032 #if GTK_CHECK_VERSION(2,6,0)
4121 gtk_list_store_insert_with_values(ls, &iter, 4033 gtk_list_store_insert_with_values(ls, &iter,
4122 /* 4034 /*
4123 * The GTK docs are mute about the effects of the "row" value for performance. 4035 * The GTK docs are mute about the effects of the "row" value for performance.
4130 CHAT_USERS_ICON_STOCK_COLUMN, stock, 4042 CHAT_USERS_ICON_STOCK_COLUMN, stock,
4131 CHAT_USERS_ALIAS_COLUMN, alias, 4043 CHAT_USERS_ALIAS_COLUMN, alias,
4132 CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, 4044 CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
4133 CHAT_USERS_NAME_COLUMN, name, 4045 CHAT_USERS_NAME_COLUMN, name,
4134 CHAT_USERS_FLAGS_COLUMN, flags, 4046 CHAT_USERS_FLAGS_COLUMN, flags,
4135 CHAT_USERS_COLOR_COLUMN, color,
4136 CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, 4047 CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
4137 -1); 4048 -1);
4138 #else 4049 #else
4139 gtk_list_store_append(ls, &iter); 4050 gtk_list_store_append(ls, &iter);
4140 gtk_list_store_set(ls, &iter, 4051 gtk_list_store_set(ls, &iter,
4141 CHAT_USERS_ICON_STOCK_COLUMN, stock, 4052 CHAT_USERS_ICON_STOCK_COLUMN, stock,
4142 CHAT_USERS_ALIAS_COLUMN, alias, 4053 CHAT_USERS_ALIAS_COLUMN, alias,
4143 CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, 4054 CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
4144 CHAT_USERS_NAME_COLUMN, name, 4055 CHAT_USERS_NAME_COLUMN, name,
4145 CHAT_USERS_FLAGS_COLUMN, flags, 4056 CHAT_USERS_FLAGS_COLUMN, flags,
4146 CHAT_USERS_COLOR_COLUMN, color,
4147 CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, 4057 CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
4148 -1); 4058 -1);
4149 #endif 4059 #endif
4150 4060
4151 if (is_me && color)
4152 gdk_color_free(color);
4153 g_free(alias_key); 4061 g_free(alias_key);
4154 } 4062 }
4155 4063
4156 static void 4064 static void
4157 tab_complete_process_item(int *most_matched, char *entered, gsize entered_bytes, char **partial, char *nick_partial, 4065 tab_complete_process_item(int *most_matched, char *entered, gsize entered_bytes, char **partial, char *nick_partial,
4545 buddy_cb_common(PurpleBuddy *buddy, PurpleConversation *conv, gboolean is_buddy) 4453 buddy_cb_common(PurpleBuddy *buddy, PurpleConversation *conv, gboolean is_buddy)
4546 { 4454 {
4547 GtkTreeModel *model; 4455 GtkTreeModel *model;
4548 char *normalized_name; 4456 char *normalized_name;
4549 GtkTreeIter iter; 4457 GtkTreeIter iter;
4550 GtkTextTag *texttag;
4551 int f; 4458 int f;
4552 4459
4553 g_return_if_fail(buddy != NULL); 4460 g_return_if_fail(buddy != NULL);
4554 g_return_if_fail(conv != NULL); 4461 g_return_if_fail(conv != NULL);
4555 4462
4584 4491
4585 g_free(normalized_name); 4492 g_free(normalized_name);
4586 4493
4587 blist_node_aliased_cb((PurpleBlistNode *)buddy, NULL, conv); 4494 blist_node_aliased_cb((PurpleBlistNode *)buddy, NULL, conv);
4588 4495
4589 texttag = get_buddy_tag(conv, purple_buddy_get_name(buddy), 0, FALSE); /* XXX: do we want the normalized name? */
4590 if (texttag) {
4591 g_object_set(texttag, "weight", is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, NULL);
4592 }
4593 } 4496 }
4594 4497
4595 static void 4498 static void
4596 buddy_added_cb(PurpleBlistNode *node, PurpleConversation *conv) 4499 buddy_added_cb(PurpleBlistNode *node, PurpleConversation *conv)
4597 { 4500 {
4619 { 4522 {
4620 g_signal_emit_by_name(gtkconv->entry, "message_send"); 4523 g_signal_emit_by_name(gtkconv->entry, "message_send");
4621 } 4524 }
4622 4525
4623 static void 4526 static void
4624 entry_popup_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data) 4527 entry_popup_menu_cb(GtkIMHtml *imhtml /* this is for ->entry, fine! */, GtkMenu *menu, gpointer data)
4625 { 4528 {
4626 GtkWidget *menuitem; 4529 GtkWidget *menuitem;
4627 PidginConversation *gtkconv = data; 4530 PidginConversation *gtkconv = data;
4628 4531
4629 g_return_if_fail(menu != NULL); 4532 g_return_if_fail(menu != NULL);
4647 GtkTextIter iter; 4550 GtkTextIter iter;
4648 int lines; 4551 int lines;
4649 GdkRectangle oneline; 4552 GdkRectangle oneline;
4650 int height, diff; 4553 int height, diff;
4651 int pad_top, pad_inside, pad_bottom; 4554 int pad_top, pad_inside, pad_bottom;
4652 int total_height = (gtkconv->imhtml->allocation.height + gtkconv->entry->allocation.height); 4555 int total_height = (gtkconv->webview->allocation.height + gtkconv->entry->allocation.height);
4653 int max_height = total_height / 2; 4556 int max_height = total_height / 2;
4654 int min_lines = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines"); 4557 int min_lines = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines");
4655 int min_height; 4558 int min_height;
4656 4559
4657 pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry)); 4560 pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
4886 4789
4887 conv = gtkconv->active_conv; 4790 conv = gtkconv->active_conv;
4888 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { 4791 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
4889 node = (PurpleBlistNode*)(purple_blist_find_chat(conv->account, conv->name)); 4792 node = (PurpleBlistNode*)(purple_blist_find_chat(conv->account, conv->name));
4890 if (!node) 4793 if (!node)
4891 node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat"); 4794 node = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat");
4892 } else { 4795 } else {
4893 node = (PurpleBlistNode*)(purple_find_buddy(conv->account, conv->name)); 4796 node = (PurpleBlistNode*)(purple_find_buddy(conv->account, conv->name));
4894 #if 0 4797 #if 0
4895 /* Using the transient blist nodes to show the tooltip doesn't quite work yet. */ 4798 /* Using the transient blist nodes to show the tooltip doesn't quite work yet. */
4896 if (!node) 4799 if (!node)
4897 node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy"); 4800 node = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy");
4898 #endif 4801 #endif
4899 } 4802 }
4900 4803
4901 if (node) 4804 if (node)
4902 pidgin_blist_draw_tooltip(node, gtkconv->infopane); 4805 pidgin_blist_draw_tooltip(node, gtkconv->infopane);
4904 } 4807 }
4905 4808
4906 static GtkWidget * 4809 static GtkWidget *
4907 setup_common_pane(PidginConversation *gtkconv) 4810 setup_common_pane(PidginConversation *gtkconv)
4908 { 4811 {
4909 GtkWidget *vbox, *frame, *imhtml_sw, *event_box; 4812 GtkWidget *vbox, *frame, *webview_sw, *event_box;
4910 GtkCellRenderer *rend; 4813 GtkCellRenderer *rend;
4911 GtkTreePath *path; 4814 GtkTreePath *path;
4912 PurpleConversation *conv = gtkconv->active_conv; 4815 PurpleConversation *conv = gtkconv->active_conv;
4913 PurpleBuddy *buddy; 4816 PurpleBuddy *buddy;
4914 gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT); 4817 gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT);
4915 GtkPolicyType imhtml_sw_hscroll; 4818 GtkPolicyType webview_sw_hscroll;
4916 int buddyicon_size = 0; 4819 int buddyicon_size = 0;
4917 4820
4918 /* Setup the top part of the pane */ 4821 /* Setup the top part of the pane */
4919 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); 4822 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4920 gtk_widget_show(vbox); 4823 gtk_widget_show(vbox);
5006 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE); 4909 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE);
5007 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_EMBLEM_COLUMN, NULL); 4910 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_EMBLEM_COLUMN, NULL);
5008 g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); 4911 g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
5009 4912
5010 /* Setup the gtkimhtml widget */ 4913 /* Setup the gtkimhtml widget */
5011 frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw); 4914 webview_sw = gtk_scrolled_window_new (NULL, NULL);
5012 gtk_widget_set_size_request(gtkconv->imhtml, -1, 0); 4915 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(webview_sw), GTK_SHADOW_IN);
4916 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (webview_sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
4917
4918 gtkconv->webview = gtk_webview_new ();
4919 gtk_container_add (GTK_CONTAINER (webview_sw), gtkconv->webview);
4920
4921 gtk_widget_set_size_request(gtkconv->webview, -1, 0);
4922
5013 if (chat) { 4923 if (chat) {
5014 GtkWidget *hpaned; 4924 GtkWidget *hpaned;
5015 4925
5016 /* Add the topic */ 4926 /* Add the topic */
5017 setup_chat_topic(gtkconv, vbox); 4927 setup_chat_topic(gtkconv, vbox);
5018 4928
5019 /* Add the gtkimhtml frame */ 4929 /* Add the gtkimhtml frame */
5020 hpaned = gtk_hpaned_new(); 4930 hpaned = gtk_hpaned_new();
5021 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); 4931 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
5022 gtk_widget_show(hpaned); 4932 gtk_widget_show(hpaned);
5023 gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE); 4933 gtk_paned_pack1(GTK_PANED(hpaned), webview_sw, TRUE, TRUE);
5024 4934
5025 /* Now add the userlist */ 4935 /* Now add the userlist */
5026 setup_chat_userlist(gtkconv, hpaned); 4936 setup_chat_userlist(gtkconv, hpaned);
5027 } else { 4937 } else {
5028 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); 4938 gtk_box_pack_start(GTK_BOX(vbox), webview_sw, TRUE, TRUE, 0);
5029 } 4939 }
5030 gtk_widget_show(frame); 4940 gtk_widget_show_all(webview_sw);
5031 4941
5032 gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml"); 4942 gtk_widget_set_name(gtkconv->webview, "pidgin_conv_webview");
5033 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE); 4943 g_object_set_data(G_OBJECT(gtkconv->webview), "gtkconv", gtkconv);
5034 g_object_set_data(G_OBJECT(gtkconv->imhtml), "gtkconv", gtkconv); 4944
5035 4945 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(webview_sw),
5036 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw), 4946 &webview_sw_hscroll, NULL);
5037 &imhtml_sw_hscroll, NULL); 4947 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(webview_sw),
5038 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw), 4948 webview_sw_hscroll, GTK_POLICY_ALWAYS);
5039 imhtml_sw_hscroll, GTK_POLICY_ALWAYS); 4949
5040 4950 g_signal_connect_after(G_OBJECT(gtkconv->webview), "button_press_event",
5041 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
5042 G_CALLBACK(entry_stop_rclick_cb), NULL); 4951 G_CALLBACK(entry_stop_rclick_cb), NULL);
5043 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event", 4952 g_signal_connect(G_OBJECT(gtkconv->webview), "key_press_event",
5044 G_CALLBACK(refocus_entry_cb), gtkconv); 4953 G_CALLBACK(refocus_entry_cb), gtkconv);
5045 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event", 4954 g_signal_connect(G_OBJECT(gtkconv->webview), "key_release_event",
5046 G_CALLBACK(refocus_entry_cb), gtkconv); 4955 G_CALLBACK(refocus_entry_cb), gtkconv);
5047 4956
5048 gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); 4957 gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
5049 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0); 4958 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0);
5050 gtk_widget_show(gtkconv->lower_hbox); 4959 gtk_widget_show(gtkconv->lower_hbox);
5279 return FALSE; 5188 return FALSE;
5280 } 5189 }
5281 5190
5282 static void set_typing_font(GtkWidget *widget, GtkStyle *style, PidginConversation *gtkconv) 5191 static void set_typing_font(GtkWidget *widget, GtkStyle *style, PidginConversation *gtkconv)
5283 { 5192 {
5284 static PangoFontDescription *font_desc = NULL;
5285 static GdkColor *color = NULL;
5286 static gboolean enable = TRUE;
5287
5288 if (font_desc == NULL) {
5289 char *string = NULL;
5290 gtk_widget_style_get(widget,
5291 "typing-notification-font", &string,
5292 "typing-notification-color", &color,
5293 "typing-notification-enable", &enable,
5294 NULL);
5295 font_desc = pango_font_description_from_string(string);
5296 g_free(string);
5297 if (color == NULL) {
5298 GdkColor def = {0, 0x8888, 0x8888, 0x8888};
5299 color = gdk_color_copy(&def);
5300 }
5301 }
5302
5303 gtk_text_buffer_create_tag(GTK_IMHTML(widget)->text_buffer, "TYPING-NOTIFICATION",
5304 "foreground-gdk", color,
5305 "font-desc", font_desc,
5306 NULL);
5307
5308 if (!enable) {
5309 g_object_set_data(G_OBJECT(widget), "disable-typing-notification", GINT_TO_POINTER(TRUE));
5310 /* or may be 'gtkconv->disable_typing = TRUE;' instead? */
5311 }
5312
5313 g_signal_handlers_disconnect_by_func(G_OBJECT(widget), set_typing_font, gtkconv);
5314 } 5193 }
5315 5194
5316 /************************************************************************** 5195 /**************************************************************************
5317 * Conversation UI operations 5196 * Conversation UI operations
5318 **************************************************************************/ 5197 **************************************************************************/
5349 gtkconv->u.im = g_malloc0(sizeof(PidginImPane)); 5228 gtkconv->u.im = g_malloc0(sizeof(PidginImPane));
5350 } else if (conv_type == PURPLE_CONV_TYPE_CHAT) { 5229 } else if (conv_type == PURPLE_CONV_TYPE_CHAT) {
5351 gtkconv->u.chat = g_malloc0(sizeof(PidginChatPane)); 5230 gtkconv->u.chat = g_malloc0(sizeof(PidginChatPane));
5352 } 5231 }
5353 pane = setup_common_pane(gtkconv); 5232 pane = setup_common_pane(gtkconv);
5354
5355 gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml),
5356 gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE);
5357 5233
5358 if (pane == NULL) { 5234 if (pane == NULL) {
5359 if (conv_type == PURPLE_CONV_TYPE_CHAT) 5235 if (conv_type == PURPLE_CONV_TYPE_CHAT)
5360 g_free(gtkconv->u.chat); 5236 g_free(gtkconv->u.chat);
5361 else if (conv_type == PURPLE_CONV_TYPE_IM) 5237 else if (conv_type == PURPLE_CONV_TYPE_IM)
5375 gtk_drag_dest_set(pane, 5251 gtk_drag_dest_set(pane,
5376 GTK_DEST_DEFAULT_MOTION | 5252 GTK_DEST_DEFAULT_MOTION |
5377 GTK_DEST_DEFAULT_DROP, 5253 GTK_DEST_DEFAULT_DROP,
5378 te, sizeof(te) / sizeof(GtkTargetEntry), 5254 te, sizeof(te) / sizeof(GtkTargetEntry),
5379 GDK_ACTION_COPY); 5255 GDK_ACTION_COPY);
5380 gtk_drag_dest_set(gtkconv->imhtml, 0, 5256 gtk_drag_dest_set(gtkconv->webview, 0,
5381 te, sizeof(te) / sizeof(GtkTargetEntry), 5257 te, sizeof(te) / sizeof(GtkTargetEntry),
5382 GDK_ACTION_COPY); 5258 GDK_ACTION_COPY);
5383 5259
5384 gtk_drag_dest_set(gtkconv->entry, 0, 5260 gtk_drag_dest_set(gtkconv->entry, 0,
5385 te, sizeof(te) / sizeof(GtkTargetEntry), 5261 te, sizeof(te) / sizeof(GtkTargetEntry),
5387 5263
5388 g_signal_connect(G_OBJECT(pane), "button_press_event", 5264 g_signal_connect(G_OBJECT(pane), "button_press_event",
5389 G_CALLBACK(ignore_middle_click), NULL); 5265 G_CALLBACK(ignore_middle_click), NULL);
5390 g_signal_connect(G_OBJECT(pane), "drag_data_received", 5266 g_signal_connect(G_OBJECT(pane), "drag_data_received",
5391 G_CALLBACK(conv_dnd_recv), gtkconv); 5267 G_CALLBACK(conv_dnd_recv), gtkconv);
5392 g_signal_connect(G_OBJECT(gtkconv->imhtml), "drag_data_received", 5268 g_signal_connect(G_OBJECT(gtkconv->webview), "drag_data_received",
5393 G_CALLBACK(conv_dnd_recv), gtkconv); 5269 G_CALLBACK(conv_dnd_recv), gtkconv);
5394 g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received", 5270 g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received",
5395 G_CALLBACK(conv_dnd_recv), gtkconv); 5271 G_CALLBACK(conv_dnd_recv), gtkconv);
5396 5272
5397 g_signal_connect(gtkconv->imhtml, "style-set", G_CALLBACK(set_typing_font), gtkconv); 5273 g_signal_connect(gtkconv->webview, "style-set", G_CALLBACK(set_typing_font), gtkconv);
5398 5274
5399 /* Setup the container for the tab. */ 5275 /* Setup the container for the tab. */
5400 gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); 5276 gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
5401 g_object_set_data(G_OBJECT(tab_cont), "PidginConversation", gtkconv); 5277 g_object_set_data(G_OBJECT(tab_cont), "PidginConversation", gtkconv);
5402 gtk_container_set_border_width(GTK_CONTAINER(tab_cont), PIDGIN_HIG_BOX_SPACE); 5278 gtk_container_set_border_width(GTK_CONTAINER(tab_cont), PIDGIN_HIG_BOX_SPACE);
5422 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons")) 5298 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons"))
5423 gtk_widget_show(gtkconv->infopane_hbox); 5299 gtk_widget_show(gtkconv->infopane_hbox);
5424 else 5300 else
5425 gtk_widget_hide(gtkconv->infopane_hbox); 5301 gtk_widget_hide(gtkconv->infopane_hbox);
5426 5302
5427 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
5428 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_timestamps"));
5429 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
5430 purple_account_get_protocol_name(conv->account));
5431 5303
5432 g_signal_connect_swapped(G_OBJECT(pane), "focus", 5304 g_signal_connect_swapped(G_OBJECT(pane), "focus",
5433 G_CALLBACK(gtk_widget_grab_focus), 5305 G_CALLBACK(gtk_widget_grab_focus),
5434 gtkconv->entry); 5306 gtkconv->entry);
5435 5307
5438 else 5310 else
5439 pidgin_conv_placement_place(gtkconv); 5311 pidgin_conv_placement_place(gtkconv);
5440 5312
5441 if (nick_colors == NULL) { 5313 if (nick_colors == NULL) {
5442 nbr_nick_colors = NUM_NICK_COLORS; 5314 nbr_nick_colors = NUM_NICK_COLORS;
5443 nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->imhtml)->base[GTK_STATE_NORMAL]); 5315 nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->webview)->base[GTK_STATE_NORMAL]);
5444 } 5316 }
5445 5317
5446 if (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY) 5318 if (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
5447 pidgin_themes_smiley_themeize_custom(gtkconv->entry); 5319 pidgin_themes_smiley_themeize_custom(gtkconv->entry);
5448 } 5320 }
5645 } 5517 }
5646 5518
5647 return FALSE; 5519 return FALSE;
5648 } 5520 }
5649 5521
5650 static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, PurpleMessageFlags flag,
5651 gboolean create)
5652 {
5653 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
5654 GtkTextTag *buddytag;
5655 gchar *str;
5656 gboolean highlight = (flag & PURPLE_MESSAGE_NICK);
5657 GtkTextBuffer *buffer = GTK_IMHTML(gtkconv->imhtml)->text_buffer;
5658
5659 str = g_strdup_printf(highlight ? "HILIT %s" : "BUDDY %s", who);
5660
5661 buddytag = gtk_text_tag_table_lookup(
5662 gtk_text_buffer_get_tag_table(buffer), str);
5663
5664 if (buddytag == NULL && create) {
5665 if (highlight)
5666 buddytag = gtk_text_buffer_create_tag(buffer, str,
5667 "foreground", get_text_tag_color(gtk_text_tag_table_lookup(
5668 gtk_text_buffer_get_tag_table(buffer), "highlight-name")),
5669 "weight", PANGO_WEIGHT_BOLD,
5670 NULL);
5671 else
5672 buddytag = gtk_text_buffer_create_tag(
5673 buffer, str,
5674 "foreground-gdk", get_nick_color(gtkconv, who),
5675 "weight", purple_find_buddy(purple_conversation_get_account(conv), who) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
5676 NULL);
5677
5678 g_signal_connect(G_OBJECT(buddytag), "event",
5679 G_CALLBACK(buddytag_event), conv);
5680 }
5681
5682 g_free(str);
5683
5684 return buddytag;
5685 }
5686
5687 static void pidgin_conv_calculate_newday(PidginConversation *gtkconv, time_t mtime) 5522 static void pidgin_conv_calculate_newday(PidginConversation *gtkconv, time_t mtime)
5688 { 5523 {
5689 struct tm *tm = localtime(&mtime); 5524 struct tm *tm = localtime(&mtime);
5690 5525
5691 tm->tm_hour = tm->tm_min = tm->tm_sec = 0; 5526 tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
5738 PidginConversation *gtkconv; 5573 PidginConversation *gtkconv;
5739 PidginWindow *win; 5574 PidginWindow *win;
5740 PurpleConnection *gc; 5575 PurpleConnection *gc;
5741 PurpleAccount *account; 5576 PurpleAccount *account;
5742 PurplePluginProtocolInfo *prpl_info; 5577 PurplePluginProtocolInfo *prpl_info;
5743 int gtk_font_options = 0;
5744 int gtk_font_options_all = 0;
5745 int max_scrollback_lines;
5746 int line_count;
5747 char buf2[BUF_LONG]; 5578 char buf2[BUF_LONG];
5748 gboolean show_date; 5579 gboolean show_date;
5749 char *mdate; 5580 char *mdate;
5750 char *str; 5581 char *str;
5751 char *with_font_tag; 5582 char *with_font_tag;
5752 char *sml_attrib = NULL; 5583 char *sml_attrib = NULL;
5753 size_t length; 5584 size_t length;
5754 PurpleConversationType type; 5585 PurpleConversationType type;
5755 char *displaying; 5586 char *displaying;
5756 gboolean plugin_return; 5587 gboolean plugin_return;
5757 char *bracket;
5758 int tag_count = 0;
5759 gboolean is_rtl_message = FALSE; 5588 gboolean is_rtl_message = FALSE;
5760 5589
5761 g_return_if_fail(conv != NULL); 5590 g_return_if_fail(conv != NULL);
5762 gtkconv = PIDGIN_CONVERSATION(conv); 5591 gtkconv = PIDGIN_CONVERSATION(conv);
5763 g_return_if_fail(gtkconv != NULL); 5592 g_return_if_fail(gtkconv != NULL);
5811 g_free(displaying); 5640 g_free(displaying);
5812 return; 5641 return;
5813 } 5642 }
5814 length = strlen(displaying) + 1; 5643 length = strlen(displaying) + 1;
5815 5644
5816 /* Awful hack to work around GtkIMHtml's inefficient rendering of messages with lots of formatting changes.
5817 * If a message has over 100 '<' characters, strip formatting before appending it. Hopefully nobody actually
5818 * needs that much formatting, anyway.
5819 */
5820 for (bracket = strchr(displaying, '<'); bracket && *(bracket + 1); bracket = strchr(bracket + 1, '<'))
5821 tag_count++;
5822
5823 if (tag_count > 100) {
5824 char *tmp = displaying;
5825 displaying = purple_markup_strip_html(tmp);
5826 g_free(tmp);
5827 }
5828 5645
5829 win = gtkconv->win; 5646 win = gtkconv->win;
5830 prpl_info = gc ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL; 5647 prpl_info = gc ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL;
5831 5648
5832 line_count = gtk_text_buffer_get_line_count( 5649 /* if the buffer is not empty add a <br> */
5833 gtk_text_view_get_buffer(GTK_TEXT_VIEW( 5650 if (!gtk_webview_is_empty (GTK_WEBVIEW(gtkconv->webview)))
5834 gtkconv->imhtml))); 5651 gtk_webview_append_html (GTK_WEBVIEW(gtkconv->webview), "<br />");
5835
5836 max_scrollback_lines = purple_prefs_get_int(
5837 PIDGIN_PREFS_ROOT "/conversations/scrollback_lines");
5838 /* If we're sitting at more than 100 lines more than the
5839 max scrollback, trim down to max scrollback */
5840 if (max_scrollback_lines > 0
5841 && line_count > (max_scrollback_lines + 100)) {
5842 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer(
5843 GTK_TEXT_VIEW(gtkconv->imhtml));
5844 GtkTextIter start, end;
5845
5846 gtk_text_buffer_get_start_iter(text_buffer, &start);
5847 gtk_text_buffer_get_iter_at_line(text_buffer, &end,
5848 (line_count - max_scrollback_lines));
5849 gtk_imhtml_delete(GTK_IMHTML(gtkconv->imhtml), &start, &end);
5850 }
5851
5852 if (type == PURPLE_CONV_TYPE_CHAT)
5853 {
5854 /* Create anchor for user */
5855 GtkTextIter iter;
5856 char *tmp = g_strconcat("user:", name, NULL);
5857
5858 gtk_text_buffer_get_end_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)), &iter);
5859 gtk_text_buffer_create_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)),
5860 tmp, &iter, TRUE);
5861 g_free(tmp);
5862 }
5863
5864 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling"))
5865 gtk_font_options_all |= GTK_IMHTML_USE_SMOOTHSCROLLING;
5866
5867 if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
5868 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
5869 5652
5870 /* First message in a conversation. */ 5653 /* First message in a conversation. */
5871 if (gtkconv->newday == 0) 5654 if (gtkconv->newday == 0)
5872 pidgin_conv_calculate_newday(gtkconv, mtime); 5655 pidgin_conv_calculate_newday(gtkconv, mtime);
5873 5656
5899 if (mtime >= gtkconv->newday) 5682 if (mtime >= gtkconv->newday)
5900 pidgin_conv_calculate_newday(gtkconv, mtime); 5683 pidgin_conv_calculate_newday(gtkconv, mtime);
5901 5684
5902 sml_attrib = g_strdup_printf("sml=\"%s\"", purple_account_get_protocol_name(account)); 5685 sml_attrib = g_strdup_printf("sml=\"%s\"", purple_account_get_protocol_name(account));
5903 5686
5904 gtk_font_options |= GTK_IMHTML_NO_COMMENTS;
5905
5906 if ((flags & PURPLE_MESSAGE_RECV) &&
5907 !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting"))
5908 gtk_font_options |= GTK_IMHTML_NO_COLOURS | GTK_IMHTML_NO_FONTS | GTK_IMHTML_NO_SIZES | GTK_IMHTML_NO_FORMATTING;
5909
5910 /* this is gonna crash one day, I can feel it. */
5911 if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(conv->account)))->options &
5912 OPT_PROTO_USE_POINTSIZE) {
5913 gtk_font_options |= GTK_IMHTML_USE_POINTSIZE;
5914 }
5915
5916 if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY))
5917 {
5918 /* We want to see our own smileys. Need to revert it after send*/
5919 pidgin_themes_smiley_themeize_custom(gtkconv->imhtml);
5920 }
5921
5922 /* TODO: These colors should not be hardcoded so log.c can use them */ 5687 /* TODO: These colors should not be hardcoded so log.c can use them */
5923 if (flags & PURPLE_MESSAGE_RAW) { 5688 if (flags & PURPLE_MESSAGE_RAW) {
5924 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), message, gtk_font_options_all); 5689 gtk_webview_append_html (GTK_WEBVIEW(gtkconv->webview), message);
5925 } else if (flags & PURPLE_MESSAGE_SYSTEM) { 5690 } else if (flags & PURPLE_MESSAGE_SYSTEM) {
5926 g_snprintf(buf2, sizeof(buf2), 5691 g_snprintf(buf2, sizeof(buf2),
5927 "<FONT %s><FONT SIZE=\"2\"><!--%s --></FONT><B>%s</B></FONT>", 5692 "<font %s><font size=\"2\"><span class='timestamp'>%s</span></font><b>%s</b></font>",
5928 sml_attrib ? sml_attrib : "", mdate, displaying); 5693 sml_attrib ? sml_attrib : "", mdate, displaying);
5929 5694
5930 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); 5695 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2);
5931 5696
5932 } else if (flags & PURPLE_MESSAGE_ERROR) { 5697 } else if (flags & PURPLE_MESSAGE_ERROR) {
5933 g_snprintf(buf2, sizeof(buf2), 5698 g_snprintf(buf2, sizeof(buf2),
5934 "<FONT COLOR=\"#ff0000\"><FONT %s><FONT SIZE=\"2\"><!--%s --></FONT><B>%s</B></FONT></FONT>", 5699 "<font color=\"#ff0000\"><font %s><font size=\"2\"><span class='timestamp'>%s</span> </font><b>%s</b></font></font>",
5935 sml_attrib ? sml_attrib : "", mdate, displaying); 5700 sml_attrib ? sml_attrib : "", mdate, displaying);
5936 5701
5937 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); 5702 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2);
5938 5703
5939 } else if (flags & PURPLE_MESSAGE_NO_LOG) { 5704 } else if (flags & PURPLE_MESSAGE_NO_LOG) {
5940 g_snprintf(buf2, BUF_LONG, 5705 g_snprintf(buf2, BUF_LONG,
5941 "<B><FONT %s COLOR=\"#777777\">%s</FONT></B>", 5706 "<b><font %s color=\"#777777\">%s</font></b>",
5942 sml_attrib ? sml_attrib : "", displaying); 5707 sml_attrib ? sml_attrib : "", displaying);
5943 5708
5944 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); 5709 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2);
5945 } else { 5710 } else {
5946 char *new_message = g_memdup(displaying, length); 5711 char *new_message = g_memdup(displaying, length);
5947 char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup("")); 5712 char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup(""));
5948 /* The initial offset is to deal with 5713 /* The initial offset is to deal with
5949 * escaped entities making the string longer */ 5714 * escaped entities making the string longer */
5950 int tag_start_offset = 0; 5715 int tag_start_offset = 0;
5951 int tag_end_offset = 0; 5716 int tag_end_offset = 0;
5952 const char *tagname = NULL; 5717 const char *tagname = NULL;
5953
5954 GtkTextIter start, end;
5955 GtkTextMark *mark;
5956 GtkTextTag *tag;
5957 GtkTextBuffer *buffer = GTK_IMHTML(gtkconv->imhtml)->text_buffer;
5958 5718
5959 /* Enforce direction on alias */ 5719 /* Enforce direction on alias */
5960 if (is_rtl_message) 5720 if (is_rtl_message)
5961 str_embed_direction_chars(&alias_escaped); 5721 str_embed_direction_chars(&alias_escaped);
5962 5722
6014 } 5774 }
6015 } 5775 }
6016 5776
6017 g_free(alias_escaped); 5777 g_free(alias_escaped);
6018 5778
6019 if (tagname) 5779 /* timestamp */
6020 tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tagname); 5780 {
6021 else 5781 g_snprintf (buf2, BUF_LONG, "<font size='2'>%s </font>", mdate);
6022 tag = get_buddy_tag(conv, name, flags, TRUE); 5782
6023 5783 gtk_webview_append_html (GTK_WEBVIEW(gtkconv->webview), buf2);
6024 if (GTK_IMHTML(gtkconv->imhtml)->show_comments) { 5784 }
6025 /* The color for the timestamp has to be set in the font-tags, unfortunately. 5785 g_snprintf(buf2, BUF_LONG, "<font %s>%s</font> ", sml_attrib ? sml_attrib : "", str);
6026 * Applying the nick-tag to timestamps would work, but that can make it 5786 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2);
6027 * bold. I thought applying the "comment" tag again, which has "weight" set
6028 * to PANGO_WEIGHT_NORMAL, would remove the boldness. But it doesn't. So
6029 * this will have to do. I don't terribly like it. -- sadrul */
6030 const char *color = get_text_tag_color(tag);
6031 g_snprintf(buf2, BUF_LONG, "<FONT %s%s%s SIZE=\"2\"><!--%s --></FONT>",
6032 color ? "COLOR=\"" : "", color ? color : "", color ? "\"" : "", mdate);
6033 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
6034 }
6035
6036 gtk_text_buffer_get_end_iter(buffer, &end);
6037 mark = gtk_text_buffer_create_mark(buffer, NULL, &end, TRUE);
6038
6039 g_snprintf(buf2, BUF_LONG, "<FONT %s>%s</FONT> ", sml_attrib ? sml_attrib : "", str);
6040 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
6041
6042 gtk_text_buffer_get_end_iter(buffer, &end);
6043 gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
6044 gtk_text_buffer_apply_tag(buffer, tag, &start, &end);
6045 gtk_text_buffer_delete_mark(buffer, mark);
6046 5787
6047 g_free(str); 5788 g_free(str);
6048 5789
6049 if(gc){ 5790 if(gc){
6050 char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : ""); 5791 char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : "");
6051 char *post = "</font>"; 5792 char *post = "</font>";
6052 int pre_len = strlen(pre); 5793 with_font_tag = g_strdup_printf ("%s%s%s", pre, new_message, post);
6053 int post_len = strlen(post);
6054
6055 with_font_tag = g_malloc(length + pre_len + post_len + 1);
6056
6057 strcpy(with_font_tag, pre);
6058 memcpy(with_font_tag + pre_len, new_message, length);
6059 strcpy(with_font_tag + pre_len + length, post);
6060
6061 length += pre_len + post_len;
6062 g_free(pre); 5794 g_free(pre);
6063 } else 5795 } else
6064 with_font_tag = g_memdup(new_message, length); 5796 with_font_tag = g_memdup(new_message, length);
6065 5797
6066 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), 5798 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview),
6067 with_font_tag, gtk_font_options | gtk_font_options_all); 5799 with_font_tag);
6068 5800
6069 g_free(with_font_tag); 5801 g_free(with_font_tag);
6070 g_free(new_message); 5802 g_free(new_message);
6071 } 5803 }
6072 5804
6089 unseen = PIDGIN_UNSEEN_TEXT; 5821 unseen = PIDGIN_UNSEEN_TEXT;
6090 5822
6091 gtkconv_set_unseen(gtkconv, unseen); 5823 gtkconv_set_unseen(gtkconv, unseen);
6092 } 5824 }
6093 5825
6094 if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY))
6095 {
6096 /* Restore the smiley-data */
6097 pidgin_themes_smiley_themeize(gtkconv->imhtml);
6098 }
6099
6100 purple_signal_emit(pidgin_conversations_get_handle(), 5826 purple_signal_emit(pidgin_conversations_get_handle(),
6101 (type == PURPLE_CONV_TYPE_IM ? "displayed-im-msg" : "displayed-chat-msg"), 5827 (type == PURPLE_CONV_TYPE_IM ? "displayed-im-msg" : "displayed-chat-msg"),
6102 account, name, displaying, conv, flags); 5828 account, name, displaying, conv, flags);
6103 g_free(displaying); 5829 g_free(displaying);
6104 update_typing_message(gtkconv, NULL); 5830 update_typing_message(gtkconv, NULL);
6186 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter); 5912 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
6187 5913
6188 g_free(val); 5914 g_free(val);
6189 } 5915 }
6190 5916
6191 if ((tag = get_buddy_tag(conv, old_name, 0, FALSE)))
6192 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
6193 if ((tag = get_buddy_tag(conv, old_name, PURPLE_MESSAGE_NICK, FALSE)))
6194 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
6195 5917
6196 if (!purple_conv_chat_find_user(chat, old_name)) 5918 if (!purple_conv_chat_find_user(chat, old_name))
6197 return; 5919 return;
6198 5920
6199 g_return_if_fail(new_alias != NULL); 5921 g_return_if_fail(new_alias != NULL);
6247 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter); 5969 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
6248 5970
6249 g_free(val); 5971 g_free(val);
6250 } while (f); 5972 } while (f);
6251 5973
6252 if ((tag = get_buddy_tag(conv, l->data, 0, FALSE))) 5974
6253 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
6254 if ((tag = get_buddy_tag(conv, l->data, PURPLE_MESSAGE_NICK, FALSE)))
6255 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
6256 } 5975 }
6257 5976
6258 g_snprintf(tmp, sizeof(tmp), 5977 g_snprintf(tmp, sizeof(tmp),
6259 ngettext("%d person in room", "%d people in room", 5978 ngettext("%d person in room", "%d people in room",
6260 num_users), num_users); 5979 num_users), num_users);
6354 g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley); 6073 g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley);
6355 6074
6356 return TRUE; 6075 return TRUE;
6357 } 6076 }
6358 6077
6359 static gboolean
6360 pidgin_conv_custom_smiley_add(PurpleConversation *conv, const char *smile, gboolean remote)
6361 {
6362 PidginConversation *gtkconv;
6363 struct smiley_list *list;
6364 const char *sml = NULL, *conv_sml;
6365
6366 if (!conv || !smile || !*smile) {
6367 return FALSE;
6368 }
6369
6370 /* If smileys are off, return false */
6371 if (pidgin_themes_smileys_disabled())
6372 return FALSE;
6373
6374 /* If possible add this smiley to the current theme.
6375 * The addition is only temporary: custom smilies aren't saved to disk. */
6376 conv_sml = purple_account_get_protocol_name(conv->account);
6377 gtkconv = PIDGIN_CONVERSATION(conv);
6378
6379 for (list = (struct smiley_list *)current_smiley_theme->list; list; list = list->next) {
6380 if (!strcmp(list->sml, conv_sml)) {
6381 sml = list->sml;
6382 break;
6383 }
6384 }
6385
6386 if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->imhtml), sml, smile))
6387 return FALSE;
6388
6389 if (!remote) /* If it's a local custom smiley, then add it for the entry */
6390 if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->entry), sml, smile))
6391 return FALSE;
6392
6393 return TRUE;
6394 }
6395
6396 static void 6078 static void
6397 pidgin_conv_custom_smiley_write(PurpleConversation *conv, const char *smile, 6079 pidgin_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
6398 const guchar *data, gsize size) 6080 const guchar *data, gsize size)
6399 { 6081 {
6400 PidginConversation *gtkconv; 6082 return;
6401 GtkIMHtmlSmiley *smiley;
6402 GdkPixbufLoader *loader;
6403 const char *sml;
6404
6405 sml = purple_account_get_protocol_name(conv->account);
6406 gtkconv = PIDGIN_CONVERSATION(conv);
6407 smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile);
6408
6409 if (!smiley)
6410 return;
6411
6412 smiley->data = g_realloc(smiley->data, smiley->datasize + size);
6413 g_memmove((guchar *)smiley->data + smiley->datasize, data, size);
6414 smiley->datasize += size;
6415
6416 loader = smiley->loader;
6417 if (!loader)
6418 return;
6419
6420 gdk_pixbuf_loader_write(loader, data, size, NULL);
6421 } 6083 }
6422 6084
6423 static void 6085 static void
6424 pidgin_conv_custom_smiley_close(PurpleConversation *conv, const char *smile) 6086 pidgin_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
6425 { 6087 {
6426 PidginConversation *gtkconv; 6088 return;
6427 GtkIMHtmlSmiley *smiley;
6428 GdkPixbufLoader *loader;
6429 const char *sml;
6430
6431 g_return_if_fail(conv != NULL);
6432 g_return_if_fail(smile != NULL);
6433
6434 sml = purple_account_get_protocol_name(conv->account);
6435 gtkconv = PIDGIN_CONVERSATION(conv);
6436 smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile);
6437
6438 if (!smiley)
6439 return;
6440
6441 loader = smiley->loader;
6442
6443 if (!loader)
6444 return;
6445
6446
6447
6448 purple_debug_info("gtkconv", "About to close the smiley pixbuf\n");
6449
6450 gdk_pixbuf_loader_close(loader, NULL);
6451
6452 } 6089 }
6453 6090
6454 static void 6091 static void
6455 pidgin_conv_send_confirm(PurpleConversation *conv, const char *message) 6092 pidgin_conv_send_confirm(PurpleConversation *conv, const char *message)
6456 { 6093 {
6714 gtk_tooltips_set_tip(gtkconv->tooltips, gtkchat->topic_text, 6351 gtk_tooltips_set_tip(gtkconv->tooltips, gtkchat->topic_text,
6715 topic ? topic : "", NULL); 6352 topic ? topic : "", NULL);
6716 } 6353 }
6717 } 6354 }
6718 6355
6719 if (fields & PIDGIN_CONV_SMILEY_THEME)
6720 pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml);
6721 6356
6722 if ((fields & PIDGIN_CONV_COLORIZE_TITLE) || 6357 if ((fields & PIDGIN_CONV_COLORIZE_TITLE) ||
6723 (fields & PIDGIN_CONV_SET_TITLE) || 6358 (fields & PIDGIN_CONV_SET_TITLE) ||
6724 (fields & PIDGIN_CONV_TOPIC)) 6359 (fields & PIDGIN_CONV_TOPIC))
6725 { 6360 {
6940 pidgin_conv_chat_rename_user, /* chat_rename_user */ 6575 pidgin_conv_chat_rename_user, /* chat_rename_user */
6941 pidgin_conv_chat_remove_users, /* chat_remove_users */ 6576 pidgin_conv_chat_remove_users, /* chat_remove_users */
6942 pidgin_conv_chat_update_user, /* chat_update_user */ 6577 pidgin_conv_chat_update_user, /* chat_update_user */
6943 pidgin_conv_present_conversation, /* present */ 6578 pidgin_conv_present_conversation, /* present */
6944 pidgin_conv_has_focus, /* has_focus */ 6579 pidgin_conv_has_focus, /* has_focus */
6945 pidgin_conv_custom_smiley_add, /* custom_smiley_add */ 6580 NULL, /* custom_smiley_add */
6946 pidgin_conv_custom_smiley_write, /* custom_smiley_write */ 6581 pidgin_conv_custom_smiley_write, /* custom_smiley_write */
6947 pidgin_conv_custom_smiley_close, /* custom_smiley_close */ 6582 pidgin_conv_custom_smiley_close, /* custom_smiley_close */
6948 pidgin_conv_send_confirm, /* send_confirm */ 6583 pidgin_conv_send_confirm, /* send_confirm */
6949 NULL, 6584 NULL,
6950 NULL, 6585 NULL,
7347 6982
7348 gtk_check_menu_item_set_active( 6983 gtk_check_menu_item_set_active(
7349 GTK_CHECK_MENU_ITEM(win->menu.show_timestamps), 6984 GTK_CHECK_MENU_ITEM(win->menu.show_timestamps),
7350 (gboolean)GPOINTER_TO_INT(value)); 6985 (gboolean)GPOINTER_TO_INT(value));
7351 6986
7352 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
7353 (gboolean)GPOINTER_TO_INT(value));
7354 } 6987 }
7355 } 6988 }
7356 6989
7357 static void 6990 static void
7358 show_formatting_toolbar_pref_cb(const char *name, PurplePrefType type, 6991 show_formatting_toolbar_pref_cb(const char *name, PurplePrefType type,
7765 7398
7766 gtkconv->attach.timer = 0; 7399 gtkconv->attach.timer = 0;
7767 while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */ 7400 while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */
7768 PurpleConvMessage *msg = gtkconv->attach.current->data; 7401 PurpleConvMessage *msg = gtkconv->attach.current->data;
7769 if (!im && when && when < msg->when) { 7402 if (!im && when && when < msg->when) {
7770 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0); 7403 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR><HR>");
7771 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); 7404 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
7772 } 7405 }
7773 pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when); 7406 pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when);
7774 if (im) { 7407 if (im) {
7775 gtkconv->attach.current = g_list_delete_link(gtkconv->attach.current, gtkconv->attach.current); 7408 gtkconv->attach.current = g_list_delete_link(gtkconv->attach.current, gtkconv->attach.current);
7800 msgs = g_list_sort(msgs, message_compare); 7433 msgs = g_list_sort(msgs, message_compare);
7801 for (; msgs; msgs = g_list_delete_link(msgs, msgs)) { 7434 for (; msgs; msgs = g_list_delete_link(msgs, msgs)) {
7802 PurpleConvMessage *msg = msgs->data; 7435 PurpleConvMessage *msg = msgs->data;
7803 pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when); 7436 pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when);
7804 } 7437 }
7805 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0); 7438 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR><HR>");
7806 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); 7439 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
7807 } 7440 }
7808 7441
7809 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); 7442 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
7810 purple_signal_emit(pidgin_conversations_get_handle(), 7443 purple_signal_emit(pidgin_conversations_get_handle(),