| 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); |
| 959 } |
959 } |
| 960 |
960 |
| 961 static void |
961 static void |
| 962 savelog_writefile_cb(void *user_data, const char *filename) |
962 savelog_writefile_cb(void *user_data, const char *filename) |
| 963 { |
963 { |
| 964 PurpleConversation *conv = (PurpleConversation *)user_data; |
964 /* TODO: I don't know how to support this using webkit yet. */ |
| 965 FILE *fp; |
|
| 966 const char *name; |
|
| 967 char **lines; |
|
| 968 gchar *text; |
|
| 969 |
|
| 970 if ((fp = g_fopen(filename, "w+")) == NULL) { |
|
| 971 purple_notify_error(PIDGIN_CONVERSATION(conv), NULL, _("Unable to open file."), NULL); |
|
| 972 return; |
|
| 973 } |
|
| 974 |
|
| 975 name = purple_conversation_get_name(conv); |
|
| 976 fprintf(fp, "<html>\n<head>\n"); |
|
| 977 fprintf(fp, "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"); |
|
| 978 fprintf(fp, "<title>%s</title>\n</head>\n<body>\n", name); |
|
| 979 fprintf(fp, _("<h1>Conversation with %s</h1>\n"), name); |
|
| 980 |
|
| 981 lines = gtk_imhtml_get_markup_lines( |
|
| 982 GTK_IMHTML(PIDGIN_CONVERSATION(conv)->imhtml)); |
|
| 983 text = g_strjoinv("<br>\n", lines); |
|
| 984 fprintf(fp, "%s", text); |
|
| 985 g_free(text); |
|
| 986 g_strfreev(lines); |
|
| 987 |
|
| 988 fprintf(fp, "\n</body>\n</html>\n"); |
|
| 989 fclose(fp); |
|
| 990 } |
965 } |
| 991 |
966 |
| 992 /* |
967 /* |
| 993 * It would be kinda cool if this gave the option of saving a |
968 * It would be kinda cool if this gave the option of saving a |
| 994 * plaintext v. HTML file. |
969 * plaintext v. HTML file. |
| 1114 { |
1089 { |
| 1115 PidginConversation *gtkconv = iter->data; |
1090 PidginConversation *gtkconv = iter->data; |
| 1116 |
1091 |
| 1117 if (gtkconv != gtk_active_conv) |
1092 if (gtkconv != gtk_active_conv) |
| 1118 { |
1093 { |
| 1119 gtk_imhtml_search_clear(GTK_IMHTML(gtkconv->imhtml)); |
1094 webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (gtkconv->webview)); |
| 1120 } |
1095 } |
| 1121 else |
1096 else |
| 1122 { |
1097 { |
| 1123 gtk_imhtml_search_find(GTK_IMHTML(gtk_active_conv->imhtml), |
1098 webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (gtkconv->webview), |
| 1124 gtk_entry_get_text(GTK_ENTRY(s->entry))); |
1099 gtk_entry_get_text (GTK_ENTRY(s->entry)), TRUE, 0); |
| |
1100 webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (gtkconv->webview), |
| |
1101 true); |
| |
1102 webkit_web_view_search_text (WEBKIT_WEB_VIEW (gtkconv->webview), |
| |
1103 gtk_entry_get_text (GTK_ENTRY(s->entry)), FALSE, TRUE, FALSE); |
| 1125 } |
1104 } |
| 1126 } |
1105 } |
| 1127 break; |
1106 break; |
| 1128 |
1107 |
| 1129 case GTK_RESPONSE_DELETE_EVENT: |
1108 case GTK_RESPONSE_DELETE_EVENT: |
| 1130 case GTK_RESPONSE_CLOSE: |
1109 case GTK_RESPONSE_CLOSE: |
| 1131 /* clear the keyword highlighting in all the conversation windows */ |
1110 /* clear the keyword highlighting in all the conversation windows */ |
| 1132 for (iter = pidgin_conv_window_get_gtkconvs(s->gtkwin); iter; iter=iter->next) |
1111 for (iter = pidgin_conv_window_get_gtkconvs(s->gtkwin); iter; iter=iter->next) |
| 1133 { |
1112 { |
| 1134 PidginConversation *gconv = iter->data; |
1113 PidginConversation *gconv = iter->data; |
| 1135 gtk_imhtml_search_clear(GTK_IMHTML(gconv->imhtml)); |
1114 webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW(gconv->webview)); |
| 1136 } |
1115 } |
| 1137 |
1116 |
| 1138 gtk_widget_destroy(s->gtkwin->dialogs.search); |
1117 gtk_widget_destroy(s->gtkwin->dialogs.search); |
| 1139 s->gtkwin->dialogs.search = NULL; |
1118 s->gtkwin->dialogs.search = NULL; |
| 1140 g_free(s); |
1119 g_free(s); |
| 1633 purple_blist_request_add_buddy(account, name, NULL, NULL); |
1612 purple_blist_request_add_buddy(account, name, NULL, NULL); |
| 1634 |
1613 |
| 1635 gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry); |
1614 gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry); |
| 1636 } |
1615 } |
| 1637 |
1616 |
| 1638 static GtkTextMark * |
|
| 1639 get_mark_for_user(PidginConversation *gtkconv, const char *who) |
|
| 1640 { |
|
| 1641 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)); |
|
| 1642 char *tmp = g_strconcat("user:", who, NULL); |
|
| 1643 GtkTextMark *mark = gtk_text_buffer_get_mark(buf, tmp); |
|
| 1644 |
|
| 1645 g_free(tmp); |
|
| 1646 return mark; |
|
| 1647 } |
|
| 1648 |
|
| 1649 static void |
1617 static void |
| 1650 menu_last_said_cb(GtkWidget *w, PidginConversation *gtkconv) |
1618 menu_last_said_cb(GtkWidget *w, PidginConversation *gtkconv) |
| 1651 { |
1619 { |
| 1652 GtkTextMark *mark; |
1620 /* I don't know what this is! */ |
| 1653 const char *who; |
1621 return; |
| 1654 |
|
| 1655 who = g_object_get_data(G_OBJECT(w), "user_data"); |
|
| 1656 mark = get_mark_for_user(gtkconv, who); |
|
| 1657 |
|
| 1658 if (mark != NULL) |
|
| 1659 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0); |
|
| 1660 else |
|
| 1661 g_return_if_reached(); |
|
| 1662 } |
1622 } |
| 1663 |
1623 |
| 1664 static GtkWidget * |
1624 static GtkWidget * |
| 1665 create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc) |
1625 create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc) |
| 1666 { |
1626 { |
| 1771 } |
1731 } |
| 1772 |
1732 |
| 1773 button = pidgin_new_item_from_stock(menu, _("Last Said"), GTK_STOCK_INDEX, |
1733 button = pidgin_new_item_from_stock(menu, _("Last Said"), GTK_STOCK_INDEX, |
| 1774 G_CALLBACK(menu_last_said_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL); |
1734 G_CALLBACK(menu_last_said_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL); |
| 1775 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
1735 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
| 1776 if (!get_mark_for_user(PIDGIN_CONVERSATION(conv), who)) |
|
| 1777 gtk_widget_set_sensitive(button, FALSE); |
|
| 1778 |
1736 |
| 1779 if (buddy != NULL) |
1737 if (buddy != NULL) |
| 1780 { |
1738 { |
| 1781 if (purple_account_is_connected(conv->account)) |
1739 if (purple_account_is_connected(conv->account)) |
| 1782 pidgin_append_blist_node_proto_menu(menu, conv->account->gc, |
1740 pidgin_append_blist_node_proto_menu(menu, conv->account->gc, |
| 1862 |
1820 |
| 1863 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { |
1821 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { |
| 1864 chat_do_im(gtkconv, who); |
1822 chat_do_im(gtkconv, who); |
| 1865 } else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) { |
1823 } else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) { |
| 1866 /* Move to user's anchor */ |
1824 /* Move to user's anchor */ |
| 1867 GtkTextMark *mark = get_mark_for_user(gtkconv, who); |
1825 //GtkTextMark *mark = get_mark_for_user(gtkconv, who); |
| 1868 |
1826 |
| 1869 if(mark != NULL) |
1827 //if(mark != NULL) |
| 1870 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0); |
1828 // gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0); |
| 1871 } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { |
1829 } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { |
| 1872 GtkWidget *menu = create_chat_menu (conv, who, gc); |
1830 GtkWidget *menu = create_chat_menu (conv, who, gc); |
| 1873 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, |
1831 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, |
| 1874 event->button, event->time); |
1832 event->button, event->time); |
| 1875 } |
1833 } |
| 3248 conv = gtkconv->active_conv; |
3206 conv = gtkconv->active_conv; |
| 3249 |
3207 |
| 3250 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { |
3208 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { |
| 3251 chat = purple_blist_find_chat(conv->account, conv->name); |
3209 chat = purple_blist_find_chat(conv->account, conv->name); |
| 3252 |
3210 |
| 3253 if ((chat == NULL) && (gtkconv->imhtml != NULL)) { |
3211 if ((chat == NULL) && (gtkconv->webview != NULL)) { |
| 3254 chat = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat"); |
3212 chat = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat"); |
| 3255 } |
3213 } |
| 3256 |
3214 |
| 3257 if ((chat == NULL) && (gtkconv->imhtml != NULL)) { |
3215 if ((chat == NULL) && (gtkconv->webview != NULL)) { |
| 3258 GHashTable *components; |
3216 GHashTable *components; |
| 3259 PurpleAccount *account = purple_conversation_get_account(conv); |
3217 PurpleAccount *account = purple_conversation_get_account(conv); |
| 3260 PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account)); |
3218 PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account)); |
| 3261 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); |
3219 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); |
| 3262 if (purple_account_get_connection(account) != NULL && |
3220 if (purple_account_get_connection(account) != NULL && |
| 3282 buddy = purple_find_buddy(conv->account, conv->name); |
3240 buddy = purple_find_buddy(conv->account, conv->name); |
| 3283 |
3241 |
| 3284 /* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle |
3242 /* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle |
| 3285 * removing "isolated" buddy nodes well */ |
3243 * removing "isolated" buddy nodes well */ |
| 3286 if (purple_version_check(2, 0, 2) == NULL) { |
3244 if (purple_version_check(2, 0, 2) == NULL) { |
| 3287 if ((buddy == NULL) && (gtkconv->imhtml != NULL)) { |
3245 if ((buddy == NULL) && (gtkconv->webview != NULL)) { |
| 3288 buddy = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy"); |
3246 buddy = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy"); |
| 3289 } |
3247 } |
| 3290 |
3248 |
| 3291 if ((buddy == NULL) && (gtkconv->imhtml != NULL)) { |
3249 if ((buddy == NULL) && (gtkconv->webview != NULL)) { |
| 3292 buddy = purple_buddy_new(conv->account, conv->name, NULL); |
3250 buddy = purple_buddy_new(conv->account, conv->name, NULL); |
| 3293 purple_blist_node_set_flags((PurpleBlistNode *)buddy, |
3251 purple_blist_node_set_flags((PurpleBlistNode *)buddy, |
| 3294 PURPLE_BLIST_NODE_FLAG_NO_SAVE); |
3252 PURPLE_BLIST_NODE_FLAG_NO_SAVE); |
| 3295 g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_buddy", |
3253 g_object_set_data_full(G_OBJECT(gtkconv->webview), "transient_buddy", |
| 3296 buddy, (GDestroyNotify)purple_buddy_destroy); |
3254 buddy, (GDestroyNotify)purple_buddy_destroy); |
| 3297 } |
3255 } |
| 3298 } |
3256 } |
| 3299 } |
3257 } |
| 3300 |
3258 |
| 3641 #endif |
3599 #endif |
| 3642 |
3600 |
| 3643 static void |
3601 static void |
| 3644 update_typing_message(PidginConversation *gtkconv, const char *message) |
3602 update_typing_message(PidginConversation *gtkconv, const char *message) |
| 3645 { |
3603 { |
| 3646 GtkTextBuffer *buffer; |
3604 /* this is not handled at all */ |
| 3647 GtkTextMark *stmark, *enmark; |
|
| 3648 |
|
| 3649 if (g_object_get_data(G_OBJECT(gtkconv->imhtml), "disable-typing-notification")) |
|
| 3650 return; |
|
| 3651 |
|
| 3652 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)); |
|
| 3653 stmark = gtk_text_buffer_get_mark(buffer, "typing-notification-start"); |
|
| 3654 enmark = gtk_text_buffer_get_mark(buffer, "typing-notification-end"); |
|
| 3655 if (stmark && enmark) { |
|
| 3656 GtkTextIter start, end; |
|
| 3657 gtk_text_buffer_get_iter_at_mark(buffer, &start, stmark); |
|
| 3658 gtk_text_buffer_get_iter_at_mark(buffer, &end, enmark); |
|
| 3659 gtk_text_buffer_delete_mark(buffer, stmark); |
|
| 3660 gtk_text_buffer_delete_mark(buffer, enmark); |
|
| 3661 gtk_text_buffer_delete(buffer, &start, &end); |
|
| 3662 } else if (message && *message == '\n' && message[1] == ' ' && message[2] == '\0') |
|
| 3663 message = NULL; |
|
| 3664 |
|
| 3665 #ifdef RESERVE_LINE |
|
| 3666 if (!message) |
|
| 3667 message = "\n "; /* The blank space is required to avoid a GTK+/Pango bug */ |
|
| 3668 #endif |
|
| 3669 |
|
| 3670 if (message) { |
|
| 3671 GtkTextIter iter; |
|
| 3672 gtk_text_buffer_get_end_iter(buffer, &iter); |
|
| 3673 gtk_text_buffer_create_mark(buffer, "typing-notification-start", &iter, TRUE); |
|
| 3674 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, message, -1, "TYPING-NOTIFICATION", NULL); |
|
| 3675 gtk_text_buffer_get_end_iter(buffer, &iter); |
|
| 3676 gtk_text_buffer_create_mark(buffer, "typing-notification-end", &iter, TRUE); |
|
| 3677 } |
|
| 3678 } |
3605 } |
| 3679 |
3606 |
| 3680 static void |
3607 static void |
| 3681 update_typing_icon(PidginConversation *gtkconv) |
3608 update_typing_icon(PidginConversation *gtkconv) |
| 3682 { |
3609 { |
| 4015 is_buddy = cb->buddy; |
3941 is_buddy = cb->buddy; |
| 4016 |
3942 |
| 4017 tmp = g_utf8_casefold(alias, -1); |
3943 tmp = g_utf8_casefold(alias, -1); |
| 4018 alias_key = g_utf8_collate_key(tmp, -1); |
3944 alias_key = g_utf8_collate_key(tmp, -1); |
| 4019 g_free(tmp); |
3945 g_free(tmp); |
| 4020 |
|
| 4021 if (is_me) { |
|
| 4022 GtkTextTag *tag = gtk_text_tag_table_lookup( |
|
| 4023 gtk_text_buffer_get_tag_table(GTK_IMHTML(gtkconv->imhtml)->text_buffer), |
|
| 4024 "send-name"); |
|
| 4025 g_object_get(tag, "foreground-gdk", &color, NULL); |
|
| 4026 } else { |
|
| 4027 GtkTextTag *tag; |
|
| 4028 if ((tag = get_buddy_tag(conv, name, 0, FALSE))) |
|
| 4029 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL); |
|
| 4030 if ((tag = get_buddy_tag(conv, name, PURPLE_MESSAGE_NICK, FALSE))) |
|
| 4031 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL); |
|
| 4032 color = (GdkColor*)get_nick_color(gtkconv, name); |
|
| 4033 } |
|
| 4034 |
3946 |
| 4035 #if GTK_CHECK_VERSION(2,6,0) |
3947 #if GTK_CHECK_VERSION(2,6,0) |
| 4036 gtk_list_store_insert_with_values(ls, &iter, |
3948 gtk_list_store_insert_with_values(ls, &iter, |
| 4037 /* |
3949 /* |
| 4038 * The GTK docs are mute about the effects of the "row" value for performance. |
3950 * The GTK docs are mute about the effects of the "row" value for performance. |
| 4045 CHAT_USERS_ICON_STOCK_COLUMN, stock, |
3957 CHAT_USERS_ICON_STOCK_COLUMN, stock, |
| 4046 CHAT_USERS_ALIAS_COLUMN, alias, |
3958 CHAT_USERS_ALIAS_COLUMN, alias, |
| 4047 CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, |
3959 CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, |
| 4048 CHAT_USERS_NAME_COLUMN, name, |
3960 CHAT_USERS_NAME_COLUMN, name, |
| 4049 CHAT_USERS_FLAGS_COLUMN, flags, |
3961 CHAT_USERS_FLAGS_COLUMN, flags, |
| 4050 CHAT_USERS_COLOR_COLUMN, color, |
|
| 4051 CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, |
3962 CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, |
| 4052 -1); |
3963 -1); |
| 4053 #else |
3964 #else |
| 4054 gtk_list_store_append(ls, &iter); |
3965 gtk_list_store_append(ls, &iter); |
| 4055 gtk_list_store_set(ls, &iter, |
3966 gtk_list_store_set(ls, &iter, |
| 4056 CHAT_USERS_ICON_STOCK_COLUMN, stock, |
3967 CHAT_USERS_ICON_STOCK_COLUMN, stock, |
| 4057 CHAT_USERS_ALIAS_COLUMN, alias, |
3968 CHAT_USERS_ALIAS_COLUMN, alias, |
| 4058 CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, |
3969 CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, |
| 4059 CHAT_USERS_NAME_COLUMN, name, |
3970 CHAT_USERS_NAME_COLUMN, name, |
| 4060 CHAT_USERS_FLAGS_COLUMN, flags, |
3971 CHAT_USERS_FLAGS_COLUMN, flags, |
| 4061 CHAT_USERS_COLOR_COLUMN, color, |
|
| 4062 CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, |
3972 CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, |
| 4063 -1); |
3973 -1); |
| 4064 #endif |
3974 #endif |
| 4065 |
3975 |
| 4066 if (is_me && color) |
|
| 4067 gdk_color_free(color); |
|
| 4068 g_free(alias_key); |
3976 g_free(alias_key); |
| 4069 } |
3977 } |
| 4070 |
3978 |
| 4071 static void |
3979 static void |
| 4072 tab_complete_process_item(int *most_matched, char *entered, gsize entered_bytes, char **partial, char *nick_partial, |
3980 tab_complete_process_item(int *most_matched, char *entered, gsize entered_bytes, char **partial, char *nick_partial, |
| 4914 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE); |
4817 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE); |
| 4915 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_EMBLEM_COLUMN, NULL); |
4818 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_EMBLEM_COLUMN, NULL); |
| 4916 g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); |
4819 g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); |
| 4917 |
4820 |
| 4918 /* Setup the gtkimhtml widget */ |
4821 /* Setup the gtkimhtml widget */ |
| 4919 frame = pidgin_create_imhtml(FALSE, >kconv->imhtml, NULL, &imhtml_sw); |
4822 webview_sw = gtk_scrolled_window_new (NULL, NULL); |
| 4920 gtk_widget_set_size_request(gtkconv->imhtml, -1, 0); |
4823 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(webview_sw), GTK_SHADOW_IN); |
| |
4824 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (webview_sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
| |
4825 |
| |
4826 gtkconv->webview = gtk_webview_new (); |
| |
4827 gtk_container_add (GTK_CONTAINER (webview_sw), gtkconv->webview); |
| |
4828 |
| |
4829 gtk_widget_set_size_request(gtkconv->webview, -1, 0); |
| |
4830 |
| 4921 if (chat) { |
4831 if (chat) { |
| 4922 GtkWidget *hpaned; |
4832 GtkWidget *hpaned; |
| 4923 |
4833 |
| 4924 /* Add the topic */ |
4834 /* Add the topic */ |
| 4925 setup_chat_topic(gtkconv, vbox); |
4835 setup_chat_topic(gtkconv, vbox); |
| 4926 |
4836 |
| 4927 /* Add the gtkimhtml frame */ |
4837 /* Add the gtkimhtml frame */ |
| 4928 hpaned = gtk_hpaned_new(); |
4838 hpaned = gtk_hpaned_new(); |
| 4929 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); |
4839 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); |
| 4930 gtk_widget_show(hpaned); |
4840 gtk_widget_show(hpaned); |
| 4931 gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE); |
4841 gtk_paned_pack1(GTK_PANED(hpaned), webview_sw, TRUE, TRUE); |
| 4932 |
4842 |
| 4933 /* Now add the userlist */ |
4843 /* Now add the userlist */ |
| 4934 setup_chat_userlist(gtkconv, hpaned); |
4844 setup_chat_userlist(gtkconv, hpaned); |
| 4935 } else { |
4845 } else { |
| 4936 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); |
4846 gtk_box_pack_start(GTK_BOX(vbox), webview_sw, TRUE, TRUE, 0); |
| 4937 } |
4847 } |
| 4938 gtk_widget_show(frame); |
4848 gtk_widget_show_all(webview_sw); |
| 4939 |
4849 |
| 4940 gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml"); |
4850 gtk_widget_set_name(gtkconv->webview, "pidgin_conv_webview"); |
| 4941 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE); |
4851 g_object_set_data(G_OBJECT(gtkconv->webview), "gtkconv", gtkconv); |
| 4942 g_object_set_data(G_OBJECT(gtkconv->imhtml), "gtkconv", gtkconv); |
4852 |
| 4943 |
4853 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(webview_sw), |
| 4944 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw), |
4854 &webview_sw_hscroll, NULL); |
| 4945 &imhtml_sw_hscroll, NULL); |
4855 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(webview_sw), |
| 4946 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw), |
4856 webview_sw_hscroll, GTK_POLICY_ALWAYS); |
| 4947 imhtml_sw_hscroll, GTK_POLICY_ALWAYS); |
4857 |
| 4948 |
4858 g_signal_connect_after(G_OBJECT(gtkconv->webview), "button_press_event", |
| 4949 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event", |
|
| 4950 G_CALLBACK(entry_stop_rclick_cb), NULL); |
4859 G_CALLBACK(entry_stop_rclick_cb), NULL); |
| 4951 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event", |
4860 g_signal_connect(G_OBJECT(gtkconv->webview), "key_press_event", |
| 4952 G_CALLBACK(refocus_entry_cb), gtkconv); |
4861 G_CALLBACK(refocus_entry_cb), gtkconv); |
| 4953 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event", |
4862 g_signal_connect(G_OBJECT(gtkconv->webview), "key_release_event", |
| 4954 G_CALLBACK(refocus_entry_cb), gtkconv); |
4863 G_CALLBACK(refocus_entry_cb), gtkconv); |
| 4955 |
4864 |
| 4956 gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
4865 gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| 4957 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0); |
4866 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0); |
| 4958 gtk_widget_show(gtkconv->lower_hbox); |
4867 gtk_widget_show(gtkconv->lower_hbox); |
| 5187 return FALSE; |
5096 return FALSE; |
| 5188 } |
5097 } |
| 5189 |
5098 |
| 5190 static void set_typing_font(GtkWidget *widget, GtkStyle *style, PidginConversation *gtkconv) |
5099 static void set_typing_font(GtkWidget *widget, GtkStyle *style, PidginConversation *gtkconv) |
| 5191 { |
5100 { |
| 5192 static PangoFontDescription *font_desc = NULL; |
|
| 5193 static GdkColor *color = NULL; |
|
| 5194 static gboolean enable = TRUE; |
|
| 5195 |
|
| 5196 if (font_desc == NULL) { |
|
| 5197 char *string = NULL; |
|
| 5198 gtk_widget_style_get(widget, |
|
| 5199 "typing-notification-font", &string, |
|
| 5200 "typing-notification-color", &color, |
|
| 5201 "typing-notification-enable", &enable, |
|
| 5202 NULL); |
|
| 5203 font_desc = pango_font_description_from_string(string); |
|
| 5204 g_free(string); |
|
| 5205 if (color == NULL) { |
|
| 5206 GdkColor def = {0, 0x8888, 0x8888, 0x8888}; |
|
| 5207 color = gdk_color_copy(&def); |
|
| 5208 } |
|
| 5209 } |
|
| 5210 |
|
| 5211 gtk_text_buffer_create_tag(GTK_IMHTML(widget)->text_buffer, "TYPING-NOTIFICATION", |
|
| 5212 "foreground-gdk", color, |
|
| 5213 "font-desc", font_desc, |
|
| 5214 NULL); |
|
| 5215 |
|
| 5216 if (!enable) { |
|
| 5217 g_object_set_data(G_OBJECT(widget), "disable-typing-notification", GINT_TO_POINTER(TRUE)); |
|
| 5218 /* or may be 'gtkconv->disable_typing = TRUE;' instead? */ |
|
| 5219 } |
|
| 5220 |
|
| 5221 g_signal_handlers_disconnect_by_func(G_OBJECT(widget), set_typing_font, gtkconv); |
|
| 5222 } |
5101 } |
| 5223 |
5102 |
| 5224 /************************************************************************** |
5103 /************************************************************************** |
| 5225 * Conversation UI operations |
5104 * Conversation UI operations |
| 5226 **************************************************************************/ |
5105 **************************************************************************/ |
| 5283 gtk_drag_dest_set(pane, |
5159 gtk_drag_dest_set(pane, |
| 5284 GTK_DEST_DEFAULT_MOTION | |
5160 GTK_DEST_DEFAULT_MOTION | |
| 5285 GTK_DEST_DEFAULT_DROP, |
5161 GTK_DEST_DEFAULT_DROP, |
| 5286 te, sizeof(te) / sizeof(GtkTargetEntry), |
5162 te, sizeof(te) / sizeof(GtkTargetEntry), |
| 5287 GDK_ACTION_COPY); |
5163 GDK_ACTION_COPY); |
| 5288 gtk_drag_dest_set(gtkconv->imhtml, 0, |
5164 gtk_drag_dest_set(gtkconv->webview, 0, |
| 5289 te, sizeof(te) / sizeof(GtkTargetEntry), |
5165 te, sizeof(te) / sizeof(GtkTargetEntry), |
| 5290 GDK_ACTION_COPY); |
5166 GDK_ACTION_COPY); |
| 5291 |
5167 |
| 5292 gtk_drag_dest_set(gtkconv->entry, 0, |
5168 gtk_drag_dest_set(gtkconv->entry, 0, |
| 5293 te, sizeof(te) / sizeof(GtkTargetEntry), |
5169 te, sizeof(te) / sizeof(GtkTargetEntry), |
| 5295 |
5171 |
| 5296 g_signal_connect(G_OBJECT(pane), "button_press_event", |
5172 g_signal_connect(G_OBJECT(pane), "button_press_event", |
| 5297 G_CALLBACK(ignore_middle_click), NULL); |
5173 G_CALLBACK(ignore_middle_click), NULL); |
| 5298 g_signal_connect(G_OBJECT(pane), "drag_data_received", |
5174 g_signal_connect(G_OBJECT(pane), "drag_data_received", |
| 5299 G_CALLBACK(conv_dnd_recv), gtkconv); |
5175 G_CALLBACK(conv_dnd_recv), gtkconv); |
| 5300 g_signal_connect(G_OBJECT(gtkconv->imhtml), "drag_data_received", |
5176 g_signal_connect(G_OBJECT(gtkconv->webview), "drag_data_received", |
| 5301 G_CALLBACK(conv_dnd_recv), gtkconv); |
5177 G_CALLBACK(conv_dnd_recv), gtkconv); |
| 5302 g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received", |
5178 g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received", |
| 5303 G_CALLBACK(conv_dnd_recv), gtkconv); |
5179 G_CALLBACK(conv_dnd_recv), gtkconv); |
| 5304 |
5180 |
| 5305 g_signal_connect(gtkconv->imhtml, "style-set", G_CALLBACK(set_typing_font), gtkconv); |
5181 g_signal_connect(gtkconv->webview, "style-set", G_CALLBACK(set_typing_font), gtkconv); |
| 5306 |
5182 |
| 5307 /* Setup the container for the tab. */ |
5183 /* Setup the container for the tab. */ |
| 5308 gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
5184 gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| 5309 g_object_set_data(G_OBJECT(tab_cont), "PidginConversation", gtkconv); |
5185 g_object_set_data(G_OBJECT(tab_cont), "PidginConversation", gtkconv); |
| 5310 gtk_container_set_border_width(GTK_CONTAINER(tab_cont), PIDGIN_HIG_BOX_SPACE); |
5186 gtk_container_set_border_width(GTK_CONTAINER(tab_cont), PIDGIN_HIG_BOX_SPACE); |
| 5545 } |
5417 } |
| 5546 |
5418 |
| 5547 return FALSE; |
5419 return FALSE; |
| 5548 } |
5420 } |
| 5549 |
5421 |
| 5550 static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, PurpleMessageFlags flag, |
|
| 5551 gboolean create) |
|
| 5552 { |
|
| 5553 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); |
|
| 5554 GtkTextTag *buddytag; |
|
| 5555 gchar *str; |
|
| 5556 gboolean highlight = (flag & PURPLE_MESSAGE_NICK); |
|
| 5557 GtkTextBuffer *buffer = GTK_IMHTML(gtkconv->imhtml)->text_buffer; |
|
| 5558 |
|
| 5559 str = g_strdup_printf(highlight ? "HILIT %s" : "BUDDY %s", who); |
|
| 5560 |
|
| 5561 buddytag = gtk_text_tag_table_lookup( |
|
| 5562 gtk_text_buffer_get_tag_table(buffer), str); |
|
| 5563 |
|
| 5564 if (buddytag == NULL && create) { |
|
| 5565 if (highlight) |
|
| 5566 buddytag = gtk_text_buffer_create_tag(buffer, str, |
|
| 5567 "foreground", get_text_tag_color(gtk_text_tag_table_lookup( |
|
| 5568 gtk_text_buffer_get_tag_table(buffer), "highlight-name")), |
|
| 5569 "weight", PANGO_WEIGHT_BOLD, |
|
| 5570 NULL); |
|
| 5571 else |
|
| 5572 buddytag = gtk_text_buffer_create_tag( |
|
| 5573 buffer, str, |
|
| 5574 "foreground-gdk", get_nick_color(gtkconv, who), |
|
| 5575 "weight", purple_find_buddy(purple_conversation_get_account(conv), who) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, |
|
| 5576 NULL); |
|
| 5577 |
|
| 5578 g_signal_connect(G_OBJECT(buddytag), "event", |
|
| 5579 G_CALLBACK(buddytag_event), conv); |
|
| 5580 } |
|
| 5581 |
|
| 5582 g_free(str); |
|
| 5583 |
|
| 5584 return buddytag; |
|
| 5585 } |
|
| 5586 |
|
| 5587 static void pidgin_conv_calculate_newday(PidginConversation *gtkconv, time_t mtime) |
5422 static void pidgin_conv_calculate_newday(PidginConversation *gtkconv, time_t mtime) |
| 5588 { |
5423 { |
| 5589 struct tm *tm = localtime(&mtime); |
5424 struct tm *tm = localtime(&mtime); |
| 5590 |
5425 |
| 5591 tm->tm_hour = tm->tm_min = tm->tm_sec = 0; |
5426 tm->tm_hour = tm->tm_min = tm->tm_sec = 0; |
| 5638 PidginConversation *gtkconv; |
5473 PidginConversation *gtkconv; |
| 5639 PidginWindow *win; |
5474 PidginWindow *win; |
| 5640 PurpleConnection *gc; |
5475 PurpleConnection *gc; |
| 5641 PurpleAccount *account; |
5476 PurpleAccount *account; |
| 5642 PurplePluginProtocolInfo *prpl_info; |
5477 PurplePluginProtocolInfo *prpl_info; |
| 5643 int gtk_font_options = 0; |
|
| 5644 int gtk_font_options_all = 0; |
|
| 5645 int max_scrollback_lines; |
|
| 5646 int line_count; |
|
| 5647 char buf2[BUF_LONG]; |
5478 char buf2[BUF_LONG]; |
| 5648 gboolean show_date; |
5479 gboolean show_date; |
| 5649 char *mdate; |
5480 char *mdate; |
| 5650 char *str; |
5481 char *str; |
| 5651 char *with_font_tag; |
5482 char *with_font_tag; |
| 5652 char *sml_attrib = NULL; |
5483 char *sml_attrib = NULL; |
| 5653 size_t length; |
5484 size_t length; |
| 5654 PurpleConversationType type; |
5485 PurpleConversationType type; |
| 5655 char *displaying; |
5486 char *displaying; |
| 5656 gboolean plugin_return; |
5487 gboolean plugin_return; |
| 5657 char *bracket; |
|
| 5658 int tag_count = 0; |
|
| 5659 gboolean is_rtl_message = FALSE; |
5488 gboolean is_rtl_message = FALSE; |
| 5660 |
5489 |
| 5661 g_return_if_fail(conv != NULL); |
5490 g_return_if_fail(conv != NULL); |
| 5662 gtkconv = PIDGIN_CONVERSATION(conv); |
5491 gtkconv = PIDGIN_CONVERSATION(conv); |
| 5663 g_return_if_fail(gtkconv != NULL); |
5492 g_return_if_fail(gtkconv != NULL); |
| 5711 g_free(displaying); |
5540 g_free(displaying); |
| 5712 return; |
5541 return; |
| 5713 } |
5542 } |
| 5714 length = strlen(displaying) + 1; |
5543 length = strlen(displaying) + 1; |
| 5715 |
5544 |
| 5716 /* Awful hack to work around GtkIMHtml's inefficient rendering of messages with lots of formatting changes. |
|
| 5717 * If a message has over 100 '<' characters, strip formatting before appending it. Hopefully nobody actually |
|
| 5718 * needs that much formatting, anyway. |
|
| 5719 */ |
|
| 5720 for (bracket = strchr(displaying, '<'); bracket && *(bracket + 1); bracket = strchr(bracket + 1, '<')) |
|
| 5721 tag_count++; |
|
| 5722 |
|
| 5723 if (tag_count > 100) { |
|
| 5724 char *tmp = displaying; |
|
| 5725 displaying = purple_markup_strip_html(tmp); |
|
| 5726 g_free(tmp); |
|
| 5727 } |
|
| 5728 |
5545 |
| 5729 win = gtkconv->win; |
5546 win = gtkconv->win; |
| 5730 prpl_info = gc ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL; |
5547 prpl_info = gc ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL; |
| 5731 |
5548 |
| 5732 line_count = gtk_text_buffer_get_line_count( |
5549 /* if the buffer is not empty add a <br> */ |
| 5733 gtk_text_view_get_buffer(GTK_TEXT_VIEW( |
5550 if (!gtk_webview_is_empty (GTK_WEBVIEW(gtkconv->webview))) |
| 5734 gtkconv->imhtml))); |
5551 gtk_webview_append_html (GTK_WEBVIEW(gtkconv->webview), "<br />"); |
| 5735 |
|
| 5736 max_scrollback_lines = purple_prefs_get_int( |
|
| 5737 PIDGIN_PREFS_ROOT "/conversations/scrollback_lines"); |
|
| 5738 /* If we're sitting at more than 100 lines more than the |
|
| 5739 max scrollback, trim down to max scrollback */ |
|
| 5740 if (max_scrollback_lines > 0 |
|
| 5741 && line_count > (max_scrollback_lines + 100)) { |
|
| 5742 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( |
|
| 5743 GTK_TEXT_VIEW(gtkconv->imhtml)); |
|
| 5744 GtkTextIter start, end; |
|
| 5745 |
|
| 5746 gtk_text_buffer_get_start_iter(text_buffer, &start); |
|
| 5747 gtk_text_buffer_get_iter_at_line(text_buffer, &end, |
|
| 5748 (line_count - max_scrollback_lines)); |
|
| 5749 gtk_imhtml_delete(GTK_IMHTML(gtkconv->imhtml), &start, &end); |
|
| 5750 } |
|
| 5751 |
|
| 5752 if (type == PURPLE_CONV_TYPE_CHAT) |
|
| 5753 { |
|
| 5754 /* Create anchor for user */ |
|
| 5755 GtkTextIter iter; |
|
| 5756 char *tmp = g_strconcat("user:", name, NULL); |
|
| 5757 |
|
| 5758 gtk_text_buffer_get_end_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)), &iter); |
|
| 5759 gtk_text_buffer_create_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)), |
|
| 5760 tmp, &iter, TRUE); |
|
| 5761 g_free(tmp); |
|
| 5762 } |
|
| 5763 |
|
| 5764 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling")) |
|
| 5765 gtk_font_options_all |= GTK_IMHTML_USE_SMOOTHSCROLLING; |
|
| 5766 |
|
| 5767 if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)))) |
|
| 5768 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", gtk_font_options_all | GTK_IMHTML_NO_SCROLL); |
|
| 5769 |
5552 |
| 5770 /* First message in a conversation. */ |
5553 /* First message in a conversation. */ |
| 5771 if (gtkconv->newday == 0) |
5554 if (gtkconv->newday == 0) |
| 5772 pidgin_conv_calculate_newday(gtkconv, mtime); |
5555 pidgin_conv_calculate_newday(gtkconv, mtime); |
| 5773 |
5556 |
| 5799 if (mtime >= gtkconv->newday) |
5582 if (mtime >= gtkconv->newday) |
| 5800 pidgin_conv_calculate_newday(gtkconv, mtime); |
5583 pidgin_conv_calculate_newday(gtkconv, mtime); |
| 5801 |
5584 |
| 5802 sml_attrib = g_strdup_printf("sml=\"%s\"", purple_account_get_protocol_name(account)); |
5585 sml_attrib = g_strdup_printf("sml=\"%s\"", purple_account_get_protocol_name(account)); |
| 5803 |
5586 |
| 5804 gtk_font_options |= GTK_IMHTML_NO_COMMENTS; |
|
| 5805 |
|
| 5806 if ((flags & PURPLE_MESSAGE_RECV) && |
|
| 5807 !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting")) |
|
| 5808 gtk_font_options |= GTK_IMHTML_NO_COLOURS | GTK_IMHTML_NO_FONTS | GTK_IMHTML_NO_SIZES | GTK_IMHTML_NO_FORMATTING; |
|
| 5809 |
|
| 5810 /* this is gonna crash one day, I can feel it. */ |
|
| 5811 if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(conv->account)))->options & |
|
| 5812 OPT_PROTO_USE_POINTSIZE) { |
|
| 5813 gtk_font_options |= GTK_IMHTML_USE_POINTSIZE; |
|
| 5814 } |
|
| 5815 |
|
| 5816 if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)) |
|
| 5817 { |
|
| 5818 /* We want to see our own smileys. Need to revert it after send*/ |
|
| 5819 pidgin_themes_smiley_themeize_custom(gtkconv->imhtml); |
|
| 5820 } |
|
| 5821 |
|
| 5822 /* TODO: These colors should not be hardcoded so log.c can use them */ |
5587 /* TODO: These colors should not be hardcoded so log.c can use them */ |
| 5823 if (flags & PURPLE_MESSAGE_RAW) { |
5588 if (flags & PURPLE_MESSAGE_RAW) { |
| 5824 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), message, gtk_font_options_all); |
5589 gtk_webview_append_html (GTK_WEBVIEW(gtkconv->webview), message); |
| 5825 } else if (flags & PURPLE_MESSAGE_SYSTEM) { |
5590 } else if (flags & PURPLE_MESSAGE_SYSTEM) { |
| 5826 g_snprintf(buf2, sizeof(buf2), |
5591 g_snprintf(buf2, sizeof(buf2), |
| 5827 "<FONT %s><FONT SIZE=\"2\"><!--%s --></FONT><B>%s</B></FONT>", |
5592 "<font %s><font size=\"2\"><span class='timestamp'>%s</span></font><b>%s</b></font>", |
| 5828 sml_attrib ? sml_attrib : "", mdate, displaying); |
5593 sml_attrib ? sml_attrib : "", mdate, displaying); |
| 5829 |
5594 |
| 5830 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); |
5595 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2); |
| 5831 |
5596 |
| 5832 } else if (flags & PURPLE_MESSAGE_ERROR) { |
5597 } else if (flags & PURPLE_MESSAGE_ERROR) { |
| 5833 g_snprintf(buf2, sizeof(buf2), |
5598 g_snprintf(buf2, sizeof(buf2), |
| 5834 "<FONT COLOR=\"#ff0000\"><FONT %s><FONT SIZE=\"2\"><!--%s --></FONT><B>%s</B></FONT></FONT>", |
5599 "<font color=\"#ff0000\"><font %s><font size=\"2\"><span class='timestamp'>%s</span> </font><b>%s</b></font></font>", |
| 5835 sml_attrib ? sml_attrib : "", mdate, displaying); |
5600 sml_attrib ? sml_attrib : "", mdate, displaying); |
| 5836 |
5601 |
| 5837 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); |
5602 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2); |
| 5838 |
5603 |
| 5839 } else if (flags & PURPLE_MESSAGE_NO_LOG) { |
5604 } else if (flags & PURPLE_MESSAGE_NO_LOG) { |
| 5840 g_snprintf(buf2, BUF_LONG, |
5605 g_snprintf(buf2, BUF_LONG, |
| 5841 "<B><FONT %s COLOR=\"#777777\">%s</FONT></B>", |
5606 "<b><font %s color=\"#777777\">%s</font></b>", |
| 5842 sml_attrib ? sml_attrib : "", displaying); |
5607 sml_attrib ? sml_attrib : "", displaying); |
| 5843 |
5608 |
| 5844 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); |
5609 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2); |
| 5845 } else { |
5610 } else { |
| 5846 char *new_message = g_memdup(displaying, length); |
5611 char *new_message = g_memdup(displaying, length); |
| 5847 char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup("")); |
5612 char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup("")); |
| 5848 /* The initial offset is to deal with |
5613 /* The initial offset is to deal with |
| 5849 * escaped entities making the string longer */ |
5614 * escaped entities making the string longer */ |
| 5850 int tag_start_offset = 0; |
5615 int tag_start_offset = 0; |
| 5851 int tag_end_offset = 0; |
5616 int tag_end_offset = 0; |
| 5852 const char *tagname = NULL; |
5617 const char *tagname = NULL; |
| 5853 |
|
| 5854 GtkTextIter start, end; |
|
| 5855 GtkTextMark *mark; |
|
| 5856 GtkTextTag *tag; |
|
| 5857 GtkTextBuffer *buffer = GTK_IMHTML(gtkconv->imhtml)->text_buffer; |
|
| 5858 |
5618 |
| 5859 /* Enforce direction on alias */ |
5619 /* Enforce direction on alias */ |
| 5860 if (is_rtl_message) |
5620 if (is_rtl_message) |
| 5861 str_embed_direction_chars(&alias_escaped); |
5621 str_embed_direction_chars(&alias_escaped); |
| 5862 |
5622 |
| 5914 } |
5674 } |
| 5915 } |
5675 } |
| 5916 |
5676 |
| 5917 g_free(alias_escaped); |
5677 g_free(alias_escaped); |
| 5918 |
5678 |
| 5919 if (tagname) |
5679 /* timestamp */ |
| 5920 tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tagname); |
5680 { |
| 5921 else |
5681 g_snprintf (buf2, BUF_LONG, "<font size='2'>%s </font>", mdate); |
| 5922 tag = get_buddy_tag(conv, name, flags, TRUE); |
5682 |
| 5923 |
5683 gtk_webview_append_html (GTK_WEBVIEW(gtkconv->webview), buf2); |
| 5924 if (GTK_IMHTML(gtkconv->imhtml)->show_comments) { |
5684 } |
| 5925 /* The color for the timestamp has to be set in the font-tags, unfortunately. |
5685 g_snprintf(buf2, BUF_LONG, "<font %s>%s</font> ", sml_attrib ? sml_attrib : "", str); |
| 5926 * Applying the nick-tag to timestamps would work, but that can make it |
5686 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2); |
| 5927 * bold. I thought applying the "comment" tag again, which has "weight" set |
|
| 5928 * to PANGO_WEIGHT_NORMAL, would remove the boldness. But it doesn't. So |
|
| 5929 * this will have to do. I don't terribly like it. -- sadrul */ |
|
| 5930 const char *color = get_text_tag_color(tag); |
|
| 5931 g_snprintf(buf2, BUF_LONG, "<FONT %s%s%s SIZE=\"2\"><!--%s --></FONT>", |
|
| 5932 color ? "COLOR=\"" : "", color ? color : "", color ? "\"" : "", mdate); |
|
| 5933 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL); |
|
| 5934 } |
|
| 5935 |
|
| 5936 gtk_text_buffer_get_end_iter(buffer, &end); |
|
| 5937 mark = gtk_text_buffer_create_mark(buffer, NULL, &end, TRUE); |
|
| 5938 |
|
| 5939 g_snprintf(buf2, BUF_LONG, "<FONT %s>%s</FONT> ", sml_attrib ? sml_attrib : "", str); |
|
| 5940 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL); |
|
| 5941 |
|
| 5942 gtk_text_buffer_get_end_iter(buffer, &end); |
|
| 5943 gtk_text_buffer_get_iter_at_mark(buffer, &start, mark); |
|
| 5944 gtk_text_buffer_apply_tag(buffer, tag, &start, &end); |
|
| 5945 gtk_text_buffer_delete_mark(buffer, mark); |
|
| 5946 |
5687 |
| 5947 g_free(str); |
5688 g_free(str); |
| 5948 |
5689 |
| 5949 if(gc){ |
5690 if(gc){ |
| 5950 char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : ""); |
5691 char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : ""); |
| 5951 char *post = "</font>"; |
5692 char *post = "</font>"; |
| 5952 int pre_len = strlen(pre); |
5693 with_font_tag = g_strdup_printf ("%s%s%s", pre, new_message, post); |
| 5953 int post_len = strlen(post); |
|
| 5954 |
|
| 5955 with_font_tag = g_malloc(length + pre_len + post_len + 1); |
|
| 5956 |
|
| 5957 strcpy(with_font_tag, pre); |
|
| 5958 memcpy(with_font_tag + pre_len, new_message, length); |
|
| 5959 strcpy(with_font_tag + pre_len + length, post); |
|
| 5960 |
|
| 5961 length += pre_len + post_len; |
|
| 5962 g_free(pre); |
5694 g_free(pre); |
| 5963 } else |
5695 } else |
| 5964 with_font_tag = g_memdup(new_message, length); |
5696 with_font_tag = g_memdup(new_message, length); |
| 5965 |
5697 |
| 5966 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), |
5698 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), |
| 5967 with_font_tag, gtk_font_options | gtk_font_options_all); |
5699 with_font_tag); |
| 5968 |
5700 |
| 5969 g_free(with_font_tag); |
5701 g_free(with_font_tag); |
| 5970 g_free(new_message); |
5702 g_free(new_message); |
| 5971 } |
5703 } |
| 5972 |
5704 |
| 6254 g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley); |
5973 g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley); |
| 6255 |
5974 |
| 6256 return TRUE; |
5975 return TRUE; |
| 6257 } |
5976 } |
| 6258 |
5977 |
| 6259 static gboolean |
|
| 6260 pidgin_conv_custom_smiley_add(PurpleConversation *conv, const char *smile, gboolean remote) |
|
| 6261 { |
|
| 6262 PidginConversation *gtkconv; |
|
| 6263 struct smiley_list *list; |
|
| 6264 const char *sml = NULL, *conv_sml; |
|
| 6265 |
|
| 6266 if (!conv || !smile || !*smile) { |
|
| 6267 return FALSE; |
|
| 6268 } |
|
| 6269 |
|
| 6270 /* If smileys are off, return false */ |
|
| 6271 if (pidgin_themes_smileys_disabled()) |
|
| 6272 return FALSE; |
|
| 6273 |
|
| 6274 /* If possible add this smiley to the current theme. |
|
| 6275 * The addition is only temporary: custom smilies aren't saved to disk. */ |
|
| 6276 conv_sml = purple_account_get_protocol_name(conv->account); |
|
| 6277 gtkconv = PIDGIN_CONVERSATION(conv); |
|
| 6278 |
|
| 6279 for (list = (struct smiley_list *)current_smiley_theme->list; list; list = list->next) { |
|
| 6280 if (!strcmp(list->sml, conv_sml)) { |
|
| 6281 sml = list->sml; |
|
| 6282 break; |
|
| 6283 } |
|
| 6284 } |
|
| 6285 |
|
| 6286 if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->imhtml), sml, smile)) |
|
| 6287 return FALSE; |
|
| 6288 |
|
| 6289 if (!remote) /* If it's a local custom smiley, then add it for the entry */ |
|
| 6290 if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->entry), sml, smile)) |
|
| 6291 return FALSE; |
|
| 6292 |
|
| 6293 return TRUE; |
|
| 6294 } |
|
| 6295 |
|
| 6296 static void |
5978 static void |
| 6297 pidgin_conv_custom_smiley_write(PurpleConversation *conv, const char *smile, |
5979 pidgin_conv_custom_smiley_write(PurpleConversation *conv, const char *smile, |
| 6298 const guchar *data, gsize size) |
5980 const guchar *data, gsize size) |
| 6299 { |
5981 { |
| 6300 PidginConversation *gtkconv; |
5982 return; |
| 6301 GtkIMHtmlSmiley *smiley; |
|
| 6302 GdkPixbufLoader *loader; |
|
| 6303 const char *sml; |
|
| 6304 |
|
| 6305 sml = purple_account_get_protocol_name(conv->account); |
|
| 6306 gtkconv = PIDGIN_CONVERSATION(conv); |
|
| 6307 smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile); |
|
| 6308 |
|
| 6309 if (!smiley) |
|
| 6310 return; |
|
| 6311 |
|
| 6312 smiley->data = g_realloc(smiley->data, smiley->datasize + size); |
|
| 6313 g_memmove((guchar *)smiley->data + smiley->datasize, data, size); |
|
| 6314 smiley->datasize += size; |
|
| 6315 |
|
| 6316 loader = smiley->loader; |
|
| 6317 if (!loader) |
|
| 6318 return; |
|
| 6319 |
|
| 6320 gdk_pixbuf_loader_write(loader, data, size, NULL); |
|
| 6321 } |
5983 } |
| 6322 |
5984 |
| 6323 static void |
5985 static void |
| 6324 pidgin_conv_custom_smiley_close(PurpleConversation *conv, const char *smile) |
5986 pidgin_conv_custom_smiley_close(PurpleConversation *conv, const char *smile) |
| 6325 { |
5987 { |
| 6326 PidginConversation *gtkconv; |
5988 return; |
| 6327 GtkIMHtmlSmiley *smiley; |
|
| 6328 GdkPixbufLoader *loader; |
|
| 6329 const char *sml; |
|
| 6330 |
|
| 6331 g_return_if_fail(conv != NULL); |
|
| 6332 g_return_if_fail(smile != NULL); |
|
| 6333 |
|
| 6334 sml = purple_account_get_protocol_name(conv->account); |
|
| 6335 gtkconv = PIDGIN_CONVERSATION(conv); |
|
| 6336 smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile); |
|
| 6337 |
|
| 6338 if (!smiley) |
|
| 6339 return; |
|
| 6340 |
|
| 6341 loader = smiley->loader; |
|
| 6342 |
|
| 6343 if (!loader) |
|
| 6344 return; |
|
| 6345 |
|
| 6346 |
|
| 6347 |
|
| 6348 purple_debug_info("gtkconv", "About to close the smiley pixbuf\n"); |
|
| 6349 |
|
| 6350 gdk_pixbuf_loader_close(loader, NULL); |
|
| 6351 |
|
| 6352 } |
5989 } |
| 6353 |
5990 |
| 6354 static void |
5991 static void |
| 6355 pidgin_conv_send_confirm(PurpleConversation *conv, const char *message) |
5992 pidgin_conv_send_confirm(PurpleConversation *conv, const char *message) |
| 6356 { |
5993 { |
| 6870 pidgin_conv_chat_rename_user, /* chat_rename_user */ |
6505 pidgin_conv_chat_rename_user, /* chat_rename_user */ |
| 6871 pidgin_conv_chat_remove_users, /* chat_remove_users */ |
6506 pidgin_conv_chat_remove_users, /* chat_remove_users */ |
| 6872 pidgin_conv_chat_update_user, /* chat_update_user */ |
6507 pidgin_conv_chat_update_user, /* chat_update_user */ |
| 6873 pidgin_conv_present_conversation, /* present */ |
6508 pidgin_conv_present_conversation, /* present */ |
| 6874 pidgin_conv_has_focus, /* has_focus */ |
6509 pidgin_conv_has_focus, /* has_focus */ |
| 6875 pidgin_conv_custom_smiley_add, /* custom_smiley_add */ |
6510 NULL, /* custom_smiley_add */ |
| 6876 pidgin_conv_custom_smiley_write, /* custom_smiley_write */ |
6511 pidgin_conv_custom_smiley_write, /* custom_smiley_write */ |
| 6877 pidgin_conv_custom_smiley_close, /* custom_smiley_close */ |
6512 pidgin_conv_custom_smiley_close, /* custom_smiley_close */ |
| 6878 pidgin_conv_send_confirm, /* send_confirm */ |
6513 pidgin_conv_send_confirm, /* send_confirm */ |
| 6879 NULL, |
6514 NULL, |
| 6880 NULL, |
6515 NULL, |
| 7672 |
7305 |
| 7673 gtkconv->attach.timer = 0; |
7306 gtkconv->attach.timer = 0; |
| 7674 while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */ |
7307 while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */ |
| 7675 PurpleConvMessage *msg = gtkconv->attach.current->data; |
7308 PurpleConvMessage *msg = gtkconv->attach.current->data; |
| 7676 if (!im && when && when < msg->when) { |
7309 if (!im && when && when < msg->when) { |
| 7677 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0); |
7310 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR><HR>"); |
| 7678 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); |
7311 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); |
| 7679 } |
7312 } |
| 7680 pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when); |
7313 pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when); |
| 7681 if (im) { |
7314 if (im) { |
| 7682 gtkconv->attach.current = g_list_delete_link(gtkconv->attach.current, gtkconv->attach.current); |
7315 gtkconv->attach.current = g_list_delete_link(gtkconv->attach.current, gtkconv->attach.current); |
| 7707 msgs = g_list_sort(msgs, message_compare); |
7340 msgs = g_list_sort(msgs, message_compare); |
| 7708 for (; msgs; msgs = g_list_delete_link(msgs, msgs)) { |
7341 for (; msgs; msgs = g_list_delete_link(msgs, msgs)) { |
| 7709 PurpleConvMessage *msg = msgs->data; |
7342 PurpleConvMessage *msg = msgs->data; |
| 7710 pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when); |
7343 pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when); |
| 7711 } |
7344 } |
| 7712 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0); |
7345 gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR><HR>"); |
| 7713 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); |
7346 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); |
| 7714 } |
7347 } |
| 7715 |
7348 |
| 7716 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); |
7349 g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); |
| 7717 purple_signal_emit(pidgin_conversations_get_handle(), |
7350 purple_signal_emit(pidgin_conversations_get_handle(), |