Tue, 14 Aug 2012 22:26:33 +0200
Merge hinted string account options from default
--- a/ChangeLog Wed Aug 08 11:22:52 2012 +0200 +++ b/ChangeLog Tue Aug 14 22:26:33 2012 +0200 @@ -1,6 +1,13 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul version 3.0.0 (??/??/????): + Pidgin: + * Support building with the GTK+ 3.x toolkit. When configuring the + build, use --with-gtk=<2|3> to determine which toolkit to use. Using + either 2 or 3 will attempt to build with specifically 2.x or 3.x + support. The default is 'auto', which will first look for 3.x + development headers and then 2.x development headers. + Finch: * Support the conversation-extended signal for extending the conversation menu. (Howard Chu) (#14818) @@ -47,6 +54,11 @@ * The Offline Message Emulation plugin now adds a note that the message was an offline message. (Flavius Anton) (#2497) +version 2.10.7: + Gadu-Gadu: + * Fix a crash at startup with large contact list. Avatar support for + buddies will be disabled till 3.0.0. (#15226, #14305) + MSN: * Fix a crash when removing a user before its icon is loaded. (Mark Barfield) (#15217)
--- a/finch/libgnt/Makefile.am Wed Aug 08 11:22:52 2012 +0200 +++ b/finch/libgnt/Makefile.am Tue Aug 14 22:26:33 2012 +0200 @@ -84,7 +84,7 @@ $(libgnt_la_headers) libgnt_la_DEPENDENCIES = -libgnt_la_LDFLAGS = -export-dynamic -version_info $(GNT_LT_VERSION_INFO) -no-undefined +libgnt_la_LDFLAGS = -export-dynamic -version-info $(GNT_LT_VERSION_INFO) -no-undefined libgnt_la_LIBADD = \ $(GLIB_LIBS) \ $(GNT_LIBS) \
--- a/libpurple/accountopt.c Wed Aug 08 11:22:52 2012 +0200 +++ b/libpurple/accountopt.c Tue Aug 14 22:26:33 2012 +0200 @@ -50,10 +50,17 @@ } default_value; - gboolean masked; /**< Whether the value entered should be - * obscured from view (for passwords and - * similar options) - */ + union + { + struct + { + gboolean masked; /**< Whether the value entered should + * be obscured from view (for + * passwords and similar options) + */ + GSList *hints; /**< List of hinted values */ + } string; + } params; }; /** @@ -177,6 +184,7 @@ if (option->type == PURPLE_PREF_STRING) { g_free(option->default_value.string); + g_slist_free_full(option->params.string.hints, &g_free); } else if (option->type == PURPLE_PREF_STRING_LIST) { @@ -221,14 +229,23 @@ } void -purple_account_option_set_masked(PurpleAccountOption *option, gboolean masked) +purple_account_option_string_set_masked(PurpleAccountOption *option, gboolean masked) { g_return_if_fail(option != NULL); g_return_if_fail(option->type == PURPLE_PREF_STRING); - option->masked = masked; + option->params.string.masked = masked; } +void +purple_account_option_string_set_hints(PurpleAccountOption *option, GSList *hints) +{ + g_return_if_fail(option != NULL); + g_return_if_fail(option->type == PURPLE_PREF_STRING); + + g_slist_free_full(option->params.string.hints, &g_free); + option->params.string.hints = hints; +} void purple_account_option_set_list(PurpleAccountOption *option, GList *values) @@ -332,12 +349,21 @@ } gboolean -purple_account_option_get_masked(const PurpleAccountOption *option) +purple_account_option_string_get_masked(const PurpleAccountOption *option) { g_return_val_if_fail(option != NULL, FALSE); g_return_val_if_fail(option->type == PURPLE_PREF_STRING, FALSE); - return option->masked; + return option->params.string.masked; +} + +const GSList * +purple_account_option_string_get_hints(const PurpleAccountOption *option) +{ + g_return_val_if_fail(option != NULL, FALSE); + g_return_val_if_fail(option->type == PURPLE_PREF_STRING, FALSE); + + return option->params.string.hints; } GList *
--- a/libpurple/accountopt.h Wed Aug 08 11:22:52 2012 +0200 +++ b/libpurple/accountopt.h Tue Aug 14 22:26:33 2012 +0200 @@ -158,7 +158,19 @@ * @param masked The masking. */ void -purple_account_option_set_masked(PurpleAccountOption *option, gboolean masked); +purple_account_option_string_set_masked(PurpleAccountOption *option, gboolean masked); + +/** + * Sets the hint list for an account option. + * + * The list passed will be owned by the account option, and the + * strings inside will be freed automatically. + * + * @param option The account option. + * @param hints The list of hints, stored as strings. + */ +void purple_account_option_string_set_hints(PurpleAccountOption *option, + GSList *hints); /** * Sets the list values for an account option. @@ -261,7 +273,16 @@ * @return %TRUE if the option's value should be obscured. */ gboolean -purple_account_option_get_masked(const PurpleAccountOption *option); +purple_account_option_string_get_masked(const PurpleAccountOption *option); + +/** + * Returns the list of hints for an account option. + * + * @param option The account option. + * + * @constreturn A list of hints, stored as strings. + */ +const GSList * purple_account_option_string_get_hints(const PurpleAccountOption *option); /** * Returns the list values for an account option.
--- a/libpurple/plugins/perl/common/AccountOpts.xs Wed Aug 08 11:22:52 2012 +0200 +++ b/libpurple/plugins/perl/common/AccountOpts.xs Tue Aug 14 22:26:33 2012 +0200 @@ -102,7 +102,7 @@ Purple::Account::Option option gboolean -purple_account_option_get_masked(option) +purple_account_option_string_get_masked(option) Purple::Account::Option option int @@ -138,7 +138,7 @@ purple_account_option_set_list(option, t_GL); void -purple_account_option_set_masked(option, masked) +purple_account_option_string_set_masked(option, masked) Purple::Account::Option option gboolean masked
--- a/pidgin/gtkaccount.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkaccount.c Tue Aug 14 22:26:33 2012 +0200 @@ -812,6 +812,7 @@ const char *str_value; gboolean bool_value; ProtocolOptEntry *opt_entry; + const GSList *str_hints; if (dialog->protocol_frame != NULL) { gtk_notebook_remove_page (GTK_NOTEBOOK(dialog->notebook), 1); @@ -912,8 +913,25 @@ purple_account_option_get_default_string(option)); } - opt_entry->widget = entry = gtk_entry_new(); - if (purple_account_option_get_masked(option)) + str_hints = purple_account_option_string_get_hints(option); + if (str_hints) + { + const GSList *hint_it = str_hints; + entry = gtk_combo_box_entry_new_text(); + while (hint_it) + { + const gchar *hint = hint_it->data; + hint_it = g_list_next(hint_it); + gtk_combo_box_append_text(GTK_COMBO_BOX(entry), hint); + } + } + else + entry = gtk_entry_new(); + + opt_entry->widget = entry; + if (purple_account_option_string_get_masked(option) && str_hints) + g_warn_if_reached(); + else if (purple_account_option_string_get_masked(option)) { gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); #if !GTK_CHECK_VERSION(2,16,0) @@ -922,7 +940,9 @@ #endif /* Less than GTK+ 2.16 */ } - if (str_value != NULL) + if (str_value != NULL && str_hints) + gtk_entry_set_text(GTK_ENTRY(GTK_BIN(entry)->child), str_value); + else gtk_entry_set_text(GTK_ENTRY(entry), str_value); title = g_strdup_printf("_%s:", @@ -1453,7 +1473,10 @@ switch (opt_entry->type) { case PURPLE_PREF_STRING: - value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget)); + if (GTK_IS_COMBO_BOX(opt_entry->widget)) + value = gtk_combo_box_get_active_text(GTK_COMBO_BOX(opt_entry->widget)); + else + value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget)); purple_account_set_string(account, opt_entry->setting, value); break;
--- a/pidgin/gtkconv.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkconv.c Tue Aug 14 22:26:33 2012 +0200 @@ -64,8 +64,6 @@ #include "gtkconv-theme.h" #include "gtkconv-theme-loader.h" #include "gtkdialogs.h" -#include "gtkimhtml.h" -#include "gtkimhtmltoolbar.h" #include "gtklog.h" #include "gtkmenutray.h" #include "gtkpounce.h" @@ -74,6 +72,7 @@ #include "gtkthemes.h" #include "gtkutils.h" #include "gtkwebview.h" +#include "gtkwebviewtoolbar.h" #include "pidginstock.h" #include "pidgintooltip.h" #include "smileyparser.h" @@ -331,20 +330,24 @@ default_formatize(PidginConversation *c) { PurpleConversation *conv = c->active_conv; - gtk_imhtml_setup_entry(GTK_IMHTML(c->entry), purple_conversation_get_features(conv)); + gtk_webview_setup_entry(GTK_WEBVIEW(c->entry), purple_conversation_get_features(conv)); } static void conversation_entry_clear(PidginConversation *gtkconv) { - GtkIMHtml *imhtml = GTK_IMHTML(gtkconv->entry); - gtk_source_undo_manager_begin_not_undoable_action(imhtml->undo_manager); - gtk_imhtml_clear(imhtml); - gtk_source_undo_manager_end_not_undoable_action(imhtml->undo_manager); -} - -static void -clear_formatting_cb(GtkIMHtml *imhtml, PidginConversation *gtkconv) + GtkWebView *webview = GTK_WEBVIEW(gtkconv->entry); + gtk_webview_load_html_string(webview, ""); +#if 0 + /* TODO WebKit */ + gtk_source_undo_manager_begin_not_undoable_action(webview->undo_manager); + gtk_webview_clear(webview); + gtk_source_undo_manager_end_not_undoable_action(webview->undo_manager); +#endif +} + +static void +clear_formatting_cb(GtkWebView *webview, PidginConversation *gtkconv) { default_formatize(gtkconv); } @@ -535,22 +538,18 @@ PidginConversation *gtkconv; char *cmd; const char *prefix; - GtkTextIter start; gboolean retval = FALSE; gtkconv = PIDGIN_CONVERSATION(conv); prefix = pidgin_get_cmd_prefix(); - cmd = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL); - gtk_text_buffer_get_start_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &start); - - if (cmd && (strncmp(cmd, prefix, strlen(prefix)) == 0) - && !gtk_text_iter_get_child_anchor(&start)) { + cmd = gtk_webview_get_body_text(GTK_WEBVIEW(gtkconv->entry)); + + if (cmd && purple_str_has_prefix(cmd, prefix)) { PurpleCmdStatus status; char *error, *cmdline, *markup, *send_history; - GtkTextIter end; - - send_history = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); + + send_history = gtk_webview_get_body_html(GTK_WEBVIEW(gtkconv->entry)); send_history_add(gtkconv, send_history); g_free(send_history); @@ -563,9 +562,8 @@ return TRUE; } - gtk_text_iter_forward_chars(&start, g_utf8_strlen(prefix, -1)); - gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &end); - markup = gtk_imhtml_get_markup_range(GTK_IMHTML(gtkconv->entry), &start, &end); + /* TODO WebKit: Cut out prefix for markup... */ + markup = gtk_webview_get_body_html(GTK_WEBVIEW(gtkconv->entry)); status = purple_cmd_do_command(conv, cmdline, markup, &error); g_free(markup); @@ -627,6 +625,7 @@ } g_free(cmd); + return retval; } @@ -653,8 +652,8 @@ if (!purple_account_is_connected(account)) return; - buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); - clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL); + buf = gtk_webview_get_body_html(GTK_WEBVIEW(gtkconv->entry)); + clean = gtk_webview_get_body_text(GTK_WEBVIEW(gtkconv->entry)); gtk_widget_grab_focus(gtkconv->entry); @@ -666,16 +665,21 @@ purple_idle_touch(); +#if 0 + /* TODO WebKit: Image stuff... */ /* XXX: is there a better way to tell if the message has images? */ - if (GTK_IMHTML(gtkconv->entry)->im_images != NULL) + if (GTK_WEBVIEW(gtkconv->entry)->im_images != NULL) flags |= PURPLE_MESSAGE_IMAGES; - - gc = purple_account_get_connection(account); +#endif + + gc = NULL/*purple_account_get_connection(account)*/; if (gc && (purple_conversation_get_features(conv) & PURPLE_CONNECTION_NO_NEWLINES)) { +#if 0 + /* TODO WebKit */ char **bufs; int i; - bufs = gtk_imhtml_get_markup_lines(GTK_IMHTML(gtkconv->entry)); + bufs = gtk_webview_get_markup_lines(GTK_WEBVIEW(gtkconv->entry)); for (i = 0; bufs[i]; i++) { send_history_add(gtkconv, bufs[i]); if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) @@ -685,6 +689,7 @@ } g_strfreev(bufs); +#endif } else { send_history_add(gtkconv, buf); @@ -1265,13 +1270,12 @@ { PidginWindow *win = data; PidginConversation *gtkconv; - GtkIMHtmlToolbar *toolbar; + GtkWebViewToolbar *toolbar; gtkconv = pidgin_conv_window_get_active_gtkconv(win); - toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), - !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link))); + toolbar = GTK_WEBVIEWTOOLBAR(gtkconv->toolbar); + + gtk_webviewtoolbar_activate(toolbar, GTK_WEBVIEWTOOLBAR_ACTION_LINK); } static void @@ -1279,15 +1283,13 @@ { PidginWindow *win = data; PidginConversation *gtkconv; - GtkIMHtmlToolbar *toolbar; + GtkWebViewToolbar *toolbar; gtkconv = pidgin_conv_window_get_active_gtkconv(win); - toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), - !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image))); -} - + toolbar = GTK_WEBVIEWTOOLBAR(gtkconv->toolbar); + + gtk_webviewtoolbar_activate(toolbar, GTK_WEBVIEWTOOLBAR_ACTION_IMAGE); +} static void menu_alias_cb(GtkAction *action, gpointer data) @@ -1619,12 +1621,17 @@ static GtkTextMark * get_mark_for_user(PidginConversation *gtkconv, const char *who) { +#if 0 + /* TODO WebKit */ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->webview)); char *tmp = g_strconcat("user:", who, NULL); GtkTextMark *mark = gtk_text_buffer_get_mark(buf, tmp); g_free(tmp); return mark; +#else + return NULL; +#endif } static void @@ -1961,7 +1968,7 @@ PidginWindow *win; int curconv; - win = gtkconv->win; + win = gtkconv->win; curconv = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook)); /* clear any tooltips */ @@ -2077,44 +2084,42 @@ break; if (!gtkconv->send_history->prev) { - GtkTextIter start, end; - g_free(gtkconv->send_history->data); - gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, - &start); - gtk_text_buffer_get_end_iter(gtkconv->entry_buffer, &end); - gtkconv->send_history->data = - gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); + gtk_webview_get_body_html(GTK_WEBVIEW(gtkconv->entry)); } if (gtkconv->send_history->next && gtkconv->send_history->next->data) { GObject *object; +#if 0 + /* TODO WebKit: maybe not necessary? */ GtkTextIter iter; GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); +#endif gtkconv->send_history = gtkconv->send_history->next; /* Block the signal to prevent application of default formatting. */ object = g_object_ref(G_OBJECT(gtkconv->entry)); - g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, - NULL, gtkconv); + g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, gtkconv); /* Clear the formatting. */ - gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry)); + gtk_webview_clear_formatting(GTK_WEBVIEW(gtkconv->entry)); /* Unblock the signal. */ - g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, - NULL, gtkconv); + g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, gtkconv); g_object_unref(object); - gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); - gtk_imhtml_append_text_with_images( - GTK_IMHTML(gtkconv->entry), gtkconv->send_history->data, - 0, NULL); + gtk_webview_load_html_string(GTK_WEBVIEW(gtkconv->entry), + gtkconv->send_history->data); /* this is mainly just a hack so the formatting at the * cursor gets picked up. */ +#if 0 + /* TODO WebKit: maybe not necessary? */ gtk_text_buffer_get_end_iter(buffer, &iter); gtk_text_buffer_move_mark_by_name(buffer, "insert", &iter); +#endif } return TRUE; @@ -2129,31 +2134,35 @@ if (gtkconv->send_history->prev && gtkconv->send_history->prev->data) { GObject *object; +#if 0 + /* TODO WebKit: maybe not necessary? */ GtkTextIter iter; GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); +#endif gtkconv->send_history = gtkconv->send_history->prev; /* Block the signal to prevent application of default formatting. */ object = g_object_ref(G_OBJECT(gtkconv->entry)); - g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, - NULL, gtkconv); + g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, gtkconv); /* Clear the formatting. */ - gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry)); + gtk_webview_clear_formatting(GTK_WEBVIEW(gtkconv->entry)); /* Unblock the signal. */ - g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, - NULL, gtkconv); + g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, gtkconv); g_object_unref(object); - gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); - gtk_imhtml_append_text_with_images( - GTK_IMHTML(gtkconv->entry), gtkconv->send_history->data, - 0, NULL); + gtk_webview_load_html_string(GTK_WEBVIEW(gtkconv->entry), + gtkconv->send_history->data); /* this is mainly just a hack so the formatting at the * cursor gets picked up. */ if (*(char *)gtkconv->send_history->data) { +#if 0 + /* TODO WebKit: maybe not necessary? */ gtk_text_buffer_get_end_iter(buffer, &iter); gtk_text_buffer_move_mark_by_name(buffer, "insert", &iter); +#endif } else { /* Restore the default formatting */ default_formatize(gtkconv); @@ -2199,6 +2208,12 @@ return TRUE; break; + case GDK_KEY_KP_Enter: + case GDK_KEY_Return: + send_cb(entry, gtkconv); + return TRUE; + break; + } } return FALSE; @@ -2240,6 +2255,7 @@ if ((event->state & GDK_CONTROL_MASK) || (event->keyval == GDK_KEY_F6) || (event->keyval == GDK_KEY_F10) || + (event->keyval == GDK_KEY_Menu) || (event->keyval == GDK_KEY_Shift_L) || (event->keyval == GDK_KEY_Shift_R) || (event->keyval == GDK_KEY_Control_L) || @@ -2264,9 +2280,7 @@ return FALSE; } - if (event->type == GDK_KEY_RELEASE) - gtk_widget_grab_focus(gtkconv->entry); - + gtk_widget_grab_focus(gtkconv->entry); gtk_widget_event(gtkconv->entry, (GdkEvent *)event); return TRUE; @@ -2280,7 +2294,7 @@ { PidginConversation *gtkconv; PurpleConversation *old_conv; - GtkIMHtml *entry; + GtkWebView *entry; const char *protocol_name; PurpleConnectionFlags features; @@ -2291,7 +2305,7 @@ purple_debug_info("gtkconv", "setting active conversation on toolbar %p\n", conv); - gtk_imhtmltoolbar_switch_active_conversation(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), + gtk_webviewtoolbar_switch_active_conversation(GTK_WEBVIEWTOOLBAR(gtkconv->toolbar), conv); if (old_conv == conv) @@ -2303,14 +2317,14 @@ purple_conversation_set_logging(conv, gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(gtkconv->win->menu.logging))); - entry = GTK_IMHTML(gtkconv->entry); + entry = GTK_WEBVIEW(gtkconv->entry); protocol_name = purple_account_get_protocol_name(purple_conversation_get_account(conv)); - gtk_imhtml_set_protocol_name(entry, protocol_name); - /* TODO WEBKIT: gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol_name); */ + gtk_webview_set_protocol_name(entry, protocol_name); + gtk_webview_set_protocol_name(GTK_WEBVIEW(gtkconv->webview), protocol_name); features = purple_conversation_get_features(conv); if (!(features & PURPLE_CONNECTION_HTML)) - gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry)); + gtk_webview_clear_formatting(GTK_WEBVIEW(gtkconv->entry)); else if (features & PURPLE_CONNECTION_FORMATTING_WBFO && !(purple_conversation_get_features(old_conv) & PURPLE_CONNECTION_FORMATTING_WBFO)) { @@ -2323,50 +2337,62 @@ gboolean bold; gboolean italic; gboolean underline; - char *fontface = gtk_imhtml_get_current_fontface(entry); - char *forecolor = gtk_imhtml_get_current_forecolor(entry); - char *backcolor = gtk_imhtml_get_current_backcolor(entry); - char *background = gtk_imhtml_get_current_background(entry); - gint fontsize = gtk_imhtml_get_current_fontsize(entry); + gboolean strike; + char *fontface = gtk_webview_get_current_fontface(entry); + char *forecolor = gtk_webview_get_current_forecolor(entry); + char *backcolor = gtk_webview_get_current_backcolor(entry); +#if 0 + /* TODO WebKit: Do we need this again? */ + char *background = gtk_webview_get_current_background(entry); +#endif + gint fontsize = gtk_webview_get_current_fontsize(entry); gboolean bold2; gboolean italic2; gboolean underline2; - - gtk_imhtml_get_current_format(entry, &bold, &italic, &underline); + gboolean strike2; + + gtk_webview_get_current_format(entry, &bold, &italic, &underline, &strike); /* Clear existing formatting */ - gtk_imhtml_clear_formatting(entry); + gtk_webview_clear_formatting(entry); /* Apply saved formatting to the whole buffer. */ - gtk_imhtml_get_current_format(entry, &bold2, &italic2, &underline2); + gtk_webview_get_current_format(entry, &bold2, &italic2, &underline2, &strike2); if (bold != bold2) - gtk_imhtml_toggle_bold(entry); + gtk_webview_toggle_bold(entry); if (italic != italic2) - gtk_imhtml_toggle_italic(entry); + gtk_webview_toggle_italic(entry); if (underline != underline2) - gtk_imhtml_toggle_underline(entry); - - gtk_imhtml_toggle_fontface(entry, fontface); + gtk_webview_toggle_underline(entry); + + if (strike != strike2) + gtk_webview_toggle_strike(entry); + + gtk_webview_toggle_fontface(entry, fontface); if (!(features & PURPLE_CONNECTION_NO_FONTSIZE)) - gtk_imhtml_font_set_size(entry, fontsize); - - gtk_imhtml_toggle_forecolor(entry, forecolor); + gtk_webview_font_set_size(entry, fontsize); + + gtk_webview_toggle_forecolor(entry, forecolor); if (!(features & PURPLE_CONNECTION_NO_BGCOLOR)) { - gtk_imhtml_toggle_backcolor(entry, backcolor); - gtk_imhtml_toggle_background(entry, background); + gtk_webview_toggle_backcolor(entry, backcolor); +#if 0 + gtk_webview_toggle_background(entry, background); +#endif } g_free(fontface); g_free(forecolor); g_free(backcolor); +#if 0 g_free(background); +#endif } else { @@ -2374,7 +2400,7 @@ * which is (obviously) a clear_formatting signal handler. However, if we're * here, we didn't call gtk_imhtml_clear_formatting() (because we want to * preserve the formatting exactly as it is), so we have to do this now. */ - gtk_imhtml_set_whole_buffer_formatting_only(entry, + gtk_webview_set_whole_buffer_formatting_only(entry, (features & PURPLE_CONNECTION_FORMATTING_WBFO)); } @@ -4276,6 +4302,8 @@ static gboolean tab_complete(PurpleConversation *conv) { +#if 0 + /* TODO WebKit */ PidginConversation *gtkconv; GtkTextIter cursor, word_start, start_buffer; int start; @@ -4446,7 +4474,7 @@ g_free(entered); g_free(partial); - +#endif return TRUE; } @@ -4706,25 +4734,23 @@ buddy_cb_common(PURPLE_BUDDY(node), conv, FALSE); } -static void send_menu_cb(GtkWidget *widget, PidginConversation *gtkconv) -{ - g_signal_emit_by_name(gtkconv->entry, "message_send"); -} - -static void -entry_popup_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data) +static void +entry_popup_menu_cb(GtkWebView *webview, GtkMenu *menu, gpointer data) { GtkWidget *menuitem; PidginConversation *gtkconv = data; + char *tmp; g_return_if_fail(menu != NULL); g_return_if_fail(gtkconv != NULL); menuitem = pidgin_new_item_from_stock(NULL, _("_Send"), NULL, - G_CALLBACK(send_menu_cb), gtkconv, - 0, 0, NULL); - if (gtk_text_buffer_get_char_count(imhtml->text_buffer) == 0) + G_CALLBACK(send_cb), gtkconv, + 0, 0, NULL); + tmp = gtk_webview_get_body_text(webview); + if (!tmp || !*tmp) gtk_widget_set_sensitive(menuitem, FALSE); + g_free(tmp); gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 0); menuitem = gtk_separator_menu_item_new(); @@ -4732,8 +4758,11 @@ gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 1); } -static gboolean resize_imhtml_cb(PidginConversation *gtkconv) -{ +static gboolean +resize_webview_cb(PidginConversation *gtkconv) +{ +#if 0 + /* TODO WebKit: entry sizing */ GtkTextBuffer *buffer; GtkTextIter iter; int lines; @@ -4797,6 +4826,8 @@ gtk_widget_set_size_request(gtkconv->lower_hbox, -1, diff + lower_hbox_allocation.height); +#endif + gtk_widget_set_size_request(gtkconv->lower_hbox, -1, -1); return FALSE; } @@ -4814,7 +4845,7 @@ conv = (PurpleConversation *)l->data; if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) - resize_imhtml_cb(PIDGIN_CONVERSATION(conv)); + resize_webview_cb(PIDGIN_CONVERSATION(conv)); l = l->next; } @@ -5409,7 +5440,7 @@ /* Add the topic */ setup_chat_topic(gtkconv, vbox); - /* Add the gtkimhtml frame */ + /* Add the gtkwebview frame */ hpaned = gtk_hpaned_new(); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); gtk_widget_show(hpaned); @@ -5439,45 +5470,48 @@ gtk_widget_show(gtkconv->lower_hbox); /* Setup the toolbar, entry widget and all signals */ - frame = pidgin_create_imhtml(TRUE, >kconv->entry, >kconv->toolbar, NULL); + frame = pidgin_create_webview(TRUE, >kconv->entry, >kconv->toolbar, NULL); gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry"); - gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry), + gtk_webview_set_protocol_name(GTK_WEBVIEW(gtkconv->entry), purple_account_get_protocol_name(purple_conversation_get_account(conv))); g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup", G_CALLBACK(entry_popup_menu_cb), gtkconv); - g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event", + g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event", G_CALLBACK(entry_key_press_cb), gtkconv); - g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send", - G_CALLBACK(send_cb), gtkconv); - g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event", +#if 0 + /* TODO WebKit: Why? */ + g_signal_connect_after(G_OBJECT(gtkconv->entry), "button-press-event", G_CALLBACK(entry_stop_rclick_cb), NULL); - - gtkconv->entry_buffer = - gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); - g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv); +#endif if (!chat) { /* For sending typing notifications for IMs */ +#if 0 + /* TODO WebKit */ g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text", G_CALLBACK(insert_text_cb), gtkconv); g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range", G_CALLBACK(delete_text_cb), gtkconv); +#endif gtkconv->u.im->typing_timer = 0; gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons"); gtkconv->u.im->show_icon = TRUE; } +#if 0 + /* TODO WebKit: sizing stuff? */ g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed", - G_CALLBACK(resize_imhtml_cb), gtkconv); + G_CALLBACK(resize_webview_cb), gtkconv); g_signal_connect_swapped(G_OBJECT(gtkconv->entry), "size-allocate", - G_CALLBACK(resize_imhtml_cb), gtkconv); + G_CALLBACK(resize_webview_cb), gtkconv); +#endif default_formatize(gtkconv); - g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear", + g_signal_connect_after(G_OBJECT(gtkconv->entry), "format-cleared", G_CALLBACK(clear_formatting_cb), gtkconv); return vbox; } @@ -5609,7 +5643,6 @@ static const GtkTargetEntry te[] = { - GTK_IMHTML_DND_TARGETS, {"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, PIDGIN_DRAG_BLIST_NODE}, {"application/x-im-contact", 0, PIDGIN_DRAG_IM_CONTACT} }; @@ -6884,31 +6917,26 @@ } static gboolean -add_custom_smiley_for_imhtml(GtkIMHtml *imhtml, const char *sml, const char *smile) -{ - GtkIMHtmlSmiley *smiley; - - smiley = gtk_imhtml_smiley_get(imhtml, sml, smile); +add_custom_smiley_for_webview(GtkWebView *webview, const char *sml, const char *smile) +{ + GtkWebViewSmiley *smiley; + + smiley = gtk_webview_smiley_find(webview, sml, smile); if (smiley) { - if (!(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) { + if (!(gtk_webview_smiley_get_flags(smiley) & GTK_WEBVIEW_SMILEY_CUSTOM)) return FALSE; - } - gtk_imhtml_smiley_reload(smiley); + + gtk_webview_smiley_reload(smiley); return TRUE; } - smiley = gtk_imhtml_smiley_create(NULL, smile, FALSE, GTK_IMHTML_SMILEY_CUSTOM); - gtk_imhtml_associate_smiley(imhtml, sml, smiley); - g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley); - - return TRUE; -} - -static gboolean -add_custom_smiley_for_webview(GtkWebView *webview, const char *sml, const char *smile) -{ - /* TODO WEBKIT: Smileys need to be added to webkit stuff */ + smiley = gtk_webview_smiley_create(NULL, smile, FALSE, + GTK_WEBVIEW_SMILEY_CUSTOM); + gtk_webview_associate_smiley(webview, sml, smiley); + g_signal_connect_swapped(webview, "destroy", + G_CALLBACK(gtk_webview_smiley_destroy), smiley); + return TRUE; } @@ -6943,7 +6971,7 @@ return FALSE; if (!remote) /* If it's a local custom smiley, then add it for the entry */ - if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->entry), sml, smile)) + if (!add_custom_smiley_for_webview(GTK_WEBVIEW(gtkconv->entry), sml, smile)) return FALSE; return TRUE; @@ -7041,7 +7069,7 @@ { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - gtk_imhtml_append_text(GTK_IMHTML(gtkconv->entry), message, 0); + gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), message); } /* @@ -7057,7 +7085,7 @@ PurpleConnection *gc; PurplePluginProtocolInfo *prpl_info = NULL; GdkPixbuf *window_icon = NULL; - GtkIMHtmlButtons buttons; + GtkWebViewButtons buttons; PurpleAccount *account; win = pidgin_conv_get_window(gtkconv); @@ -7148,18 +7176,18 @@ /* Deal with the toolbar */ if (features & PURPLE_CONNECTION_HTML) { - buttons = GTK_IMHTML_ALL; /* Everything on */ + buttons = GTK_WEBVIEW_ALL; /* Everything on */ if (features & PURPLE_CONNECTION_NO_BGCOLOR) - buttons &= ~GTK_IMHTML_BACKCOLOR; + buttons &= ~GTK_WEBVIEW_BACKCOLOR; if (features & PURPLE_CONNECTION_NO_FONTSIZE) { - buttons &= ~GTK_IMHTML_GROW; - buttons &= ~GTK_IMHTML_SHRINK; + buttons &= ~GTK_WEBVIEW_GROW; + buttons &= ~GTK_WEBVIEW_SHRINK; } if (features & PURPLE_CONNECTION_NO_URLDESC) - buttons &= ~GTK_IMHTML_LINKDESC; + buttons &= ~GTK_WEBVIEW_LINKDESC; } else { - buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE; + buttons = GTK_WEBVIEW_SMILEY | GTK_WEBVIEW_IMAGE; } if (!(prpl_info->options & OPT_PROTO_IM_IMAGE) @@ -7169,16 +7197,16 @@ } if (features & PURPLE_CONNECTION_NO_IMAGES) - buttons &= ~GTK_IMHTML_IMAGE; + buttons &= ~GTK_WEBVIEW_IMAGE; if (features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY) - buttons |= GTK_IMHTML_CUSTOM_SMILEY; + buttons |= GTK_WEBVIEW_CUSTOM_SMILEY; else - buttons &= ~GTK_IMHTML_CUSTOM_SMILEY; - - gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons); + buttons &= ~GTK_WEBVIEW_CUSTOM_SMILEY; + + gtk_webview_set_format_functions(GTK_WEBVIEW(gtkconv->entry), buttons); if (account != NULL) - gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account)); + gtk_webviewtoolbar_associate_smileys(GTK_WEBVIEWTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account)); /* Deal with menu items */ gtk_action_set_sensitive(win->menu.view_log, TRUE); @@ -7832,7 +7860,6 @@ GList *cl; PurpleConversation *conv; PidginConversation *gtkconv; - GtkSpell *spell; for (cl = purple_get_conversations(); cl != NULL; cl = cl->next) { @@ -7843,13 +7870,7 @@ gtkconv = PIDGIN_CONVERSATION(conv); - if (value) - pidgin_setup_gtkspell(GTK_TEXT_VIEW(gtkconv->entry)); - else { - spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(gtkconv->entry)); - if (spell) - gtkspell_detach(spell); - } + pidgin_webview_set_spellcheck(GTK_WEBVIEW(gtkconv->entry), value); } #endif } @@ -7901,7 +7922,7 @@ else gtk_widget_hide(gtkconv->toolbar); - g_idle_add((GSourceFunc)resize_imhtml_cb,gtkconv); + g_idle_add((GSourceFunc)resize_webview_cb, gtkconv); } } @@ -8837,8 +8858,6 @@ #include "gtkprivacy.h" #include "gtkutils.h" #include "pidginstock.h" -#include "gtkimhtml.h" -#include "gtkimhtmltoolbar.h" static void do_close(GtkWidget *w, int resp, PidginWindow *win)
--- a/pidgin/gtkconv.h Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkconv.h Tue Aug 14 22:26:33 2012 +0200 @@ -103,7 +103,6 @@ PidginConvTheme *theme; PurpleMessageFlags last_flags; GtkWidget *webview; - GtkTextBuffer *entry_buffer; GtkWidget *entry; gboolean auto_resize; /* this is set to TRUE if the conversation * is being resized by a non-user-initiated
--- a/pidgin/gtkdebug.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkdebug.c Tue Aug 14 22:26:33 2012 +0200 @@ -623,6 +623,28 @@ return FALSE; } +static void +regex_html_appended_cb(GtkWebView *webview, WebKitDOMRange *range, DebugWindow *win) +{ + if (!win || !win->window) + return; + + if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { + WebKitDOMDocument *dom; + WebKitDOMHTMLElement *body; + WebKitDOMNode *div; + + dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text)); + body = webkit_dom_document_get_body(dom); + div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body)); + + if (webkit_dom_element_webkit_matches_selector(WEBKIT_DOM_ELEMENT(div), + "body>div:not(#pause)", + NULL)) + regex_match(win, dom, div); + } +} + static DebugWindow * debug_window_new(void) { @@ -790,9 +812,13 @@ frame = pidgin_create_webview(FALSE, &win->text, NULL, NULL); gtk_webview_set_format_functions(GTK_WEBVIEW(win->text), GTK_WEBVIEW_ALL ^ GTK_WEBVIEW_SMILEY ^ GTK_WEBVIEW_IMAGE); + gtk_webview_set_autoscroll(GTK_WEBVIEW(win->text), TRUE); gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); + g_signal_connect(G_OBJECT(win->text), "html-appended", + G_CALLBACK(regex_html_appended_cb), win); + clear_cb(NULL, win); gtk_widget_show_all(win->window); @@ -990,31 +1016,7 @@ g_free(esc_s); g_free(tmp); - //XXX: gtk_webview_append_html does delayed insert of new div, which is - // needed by filtering below - //gtk_webview_append_html(GTK_WEBVIEW(debug_win->text), s); - { - WebKitDOMDocument *dom = NULL; - WebKitDOMHTMLElement *body = NULL; - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(debug_win->text)); - if (dom) - body = webkit_dom_document_get_body(dom); - if (body) - webkit_dom_html_element_insert_adjacent_html(body, "beforeend", s, NULL); - } - - if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(debug_win->filter))) { - WebKitDOMDocument *dom = NULL; - WebKitDOMHTMLElement *body = NULL; - WebKitDOMNode *div = NULL; - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(debug_win->text)); - if (dom) - body = webkit_dom_document_get_body(dom); - if (body) - div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body)); - if (div) - regex_match(debug_win, dom, div); - } + gtk_webview_append_html(GTK_WEBVIEW(debug_win->text), s); g_free(s); }
--- a/pidgin/gtkdocklet.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkdocklet.c Tue Aug 14 22:26:33 2012 +0200 @@ -323,7 +323,6 @@ } docklet_update_status(); } - } /************************************************************************** @@ -1012,6 +1011,12 @@ return &i; } +GtkStatusIcon * +pidgin_docklet_get_status_icon(void) +{ + return docklet; +} + void pidgin_docklet_init() {
--- a/pidgin/gtkdocklet.h Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkdocklet.h Tue Aug 14 22:26:33 2012 +0200 @@ -27,6 +27,11 @@ G_BEGIN_DECLS +/** + * Returns the GtkStatusIcon used for the docklet. + */ +GtkStatusIcon *pidgin_docklet_get_status_icon(void); + void pidgin_docklet_init(void); void pidgin_docklet_uninit(void); void*pidgin_docklet_get_handle(void);
--- a/pidgin/gtkimhtml.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkimhtml.c Tue Aug 14 22:26:33 2012 +0200 @@ -1713,6 +1713,7 @@ _("Enable typing notification"), TRUE, G_PARAM_READABLE)); +#if 0 binding_set = gtk_binding_set_by_class (parent_class); gtk_binding_entry_add_signal (binding_set, GDK_KEY_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD); gtk_binding_entry_add_signal (binding_set, GDK_KEY_i, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_ITALIC); @@ -1728,6 +1729,7 @@ gtk_binding_entry_add_signal (binding_set, GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0); gtk_binding_entry_add_signal (binding_set, GDK_KEY_F14, 0, "undo", 0); gtk_binding_entry_add_signal(binding_set, GDK_KEY_v, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "paste", 1, G_TYPE_STRING, "text"); +#endif } static void gtk_imhtml_init (GtkIMHtml *imhtml)
--- a/pidgin/gtkpluginpref.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkpluginpref.c Tue Aug 14 22:26:33 2012 +0200 @@ -134,8 +134,10 @@ gtk_widget_show(spacer); frame = pidgin_create_webview(TRUE, &webview, &toolbar, NULL); - if (!(format & PURPLE_STRING_FORMAT_TYPE_HTML)) + if (!(format & PURPLE_STRING_FORMAT_TYPE_HTML)) { gtk_widget_destroy(toolbar); + gtk_webview_set_format_functions(GTK_WEBVIEW(webview), 0); + } if (format & PURPLE_STRING_FORMAT_TYPE_MULTILINE) { gchar *tmp = purple_strreplace(purple_prefs_get_string(pref_name), "\n", "<br>");
--- a/pidgin/gtkstatusbox.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkstatusbox.c Tue Aug 14 22:26:33 2012 +0200 @@ -72,8 +72,8 @@ /* Timeout for typing notifications in seconds */ #define TYPING_TIMEOUT 4 -static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data); -static void imhtml_format_changed_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *data); +static void webview_changed_cb(GtkWebView *webview, void *data); +static void webview_format_changed_cb(GtkWebView *webview, GtkWebViewButtons buttons, void *data); static void remove_typing_cb(PidginStatusBox *box); static void update_size (PidginStatusBox *box); static gint get_statusbox_index(PidginStatusBox *box, PurpleSavedStatus *saved_status); @@ -274,8 +274,11 @@ break; } - gtk_imhtml_set_populate_primary_clipboard( - GTK_IMHTML(status_box->imhtml), TRUE); +#if 0 + /* TODO WebKit: Doesn't do this? */ + gtk_webview_set_populate_primary_clipboard( + GTK_WEBVIEW(status_box->webview), TRUE); +#endif if (status_no != -1) { GtkTreePath *path; @@ -291,15 +294,13 @@ if (!message || !*message) { gtk_widget_hide(status_box->vbox); - status_box->imhtml_visible = FALSE; + status_box->webview_visible = FALSE; } else { gtk_widget_show_all(status_box->vbox); - status_box->imhtml_visible = TRUE; - gtk_imhtml_clear(GTK_IMHTML(status_box->imhtml)); - gtk_imhtml_clear_formatting(GTK_IMHTML(status_box->imhtml)); - gtk_imhtml_append_text(GTK_IMHTML(status_box->imhtml), message, 0); + status_box->webview_visible = TRUE; + gtk_webview_load_html_string(GTK_WEBVIEW(status_box->webview), message); } gtk_widget_set_sensitive(GTK_WIDGET(status_box), TRUE); pidgin_status_box_refresh(status_box); @@ -929,7 +930,7 @@ message = purple_savedstatus_get_message(saved_status); /* - * If we are going to hide the imhtml, don't retain the + * If we are going to hide the webview, don't retain the * message because showing the old message later is * confusing. If we are going to set the message to a pre-set, * then we need to do this anyway @@ -937,25 +938,25 @@ * Suppress the "changed" signal because the status * was changed programmatically. */ - gtk_widget_set_sensitive(GTK_WIDGET(status_box->imhtml), FALSE); - - gtk_imhtml_clear(GTK_IMHTML(status_box->imhtml)); - gtk_imhtml_clear_formatting(GTK_IMHTML(status_box->imhtml)); + gtk_widget_set_sensitive(GTK_WIDGET(status_box->webview), FALSE); + + gtk_webview_load_html_string(GTK_WEBVIEW(status_box->webview), ""); + gtk_webview_clear_formatting(GTK_WEBVIEW(status_box->webview)); if (!purple_savedstatus_is_transient(saved_status) || !message || !*message) { - status_box->imhtml_visible = FALSE; + status_box->webview_visible = FALSE; gtk_widget_hide(status_box->vbox); } else { - status_box->imhtml_visible = TRUE; + status_box->webview_visible = TRUE; gtk_widget_show_all(status_box->vbox); - gtk_imhtml_append_text(GTK_IMHTML(status_box->imhtml), message, 0); + gtk_webview_load_html_string(GTK_WEBVIEW(status_box->webview), message); } - gtk_widget_set_sensitive(GTK_WIDGET(status_box->imhtml), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(status_box->webview), TRUE); update_size(status_box); } @@ -1132,28 +1133,35 @@ gtk_tree_view_set_search_column(GTK_TREE_VIEW(status_box->tree_view), TEXT_COLUMN); } -static gboolean combo_box_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkIMHtml *imhtml) +static gboolean +combo_box_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkWebView *webview) { pidgin_status_box_popup(PIDGIN_STATUS_BOX(w)); return TRUE; } -static gboolean imhtml_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkIMHtml *imhtml) +static gboolean +webview_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkWebView *webview) { if (event->direction == GDK_SCROLL_UP) - gtk_imhtml_page_up(imhtml); + gtk_webview_page_up(webview); else if (event->direction == GDK_SCROLL_DOWN) - gtk_imhtml_page_down(imhtml); + gtk_webview_page_down(webview); return TRUE; } -static gboolean imhtml_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box) +static gboolean +webview_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box) { - if (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_KP_Tab || event->keyval == GDK_KEY_ISO_Left_Tab) + if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) { + remove_typing_cb(status_box); + return TRUE; + } + else if (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_KP_Tab || event->keyval == GDK_KEY_ISO_Left_Tab) { /* If last inserted character is a tab, then remove the focus from here */ GtkWidget *top = gtk_widget_get_toplevel(w); - g_signal_emit_by_name(G_OBJECT(top), "move_focus", + g_signal_emit_by_name(G_OBJECT(top), "move-focus", (event->state & GDK_SHIFT_MASK) ? GTK_DIR_TAB_BACKWARD: GTK_DIR_TAB_FORWARD); return TRUE; @@ -1166,8 +1174,11 @@ { purple_timeout_remove(status_box->typing); status_box->typing = 0; - gtk_imhtml_set_populate_primary_clipboard( - GTK_IMHTML(status_box->imhtml), TRUE); +#if 0 + /* TODO WebKit: Doesn't do this? */ + gtk_webview_set_populate_primary_clipboard( + GTK_WEBVIEW(status_box->webview), TRUE); +#endif if (status_box->account != NULL) update_to_reflect_account_status(status_box, status_box->account, purple_account_get_active_status(status_box->account)); @@ -1266,18 +1277,10 @@ spellcheck_prefs_cb(const char *name, PurplePrefType type, gconstpointer value, gpointer data) { -#ifdef USE_GTKSPELL PidginStatusBox *status_box = (PidginStatusBox *)data; - if (value) - pidgin_setup_gtkspell(GTK_TEXT_VIEW(status_box->imhtml)); - else - { - GtkSpell *spell; - spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(status_box->imhtml)); - gtkspell_detach(spell); - } -#endif + pidgin_webview_set_spellcheck(GTK_WEBVIEW(status_box->webview), + (gboolean)GPOINTER_TO_INT(value)); } #if 0 @@ -1287,7 +1290,7 @@ if (event->button != 1) return FALSE; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(box->toggle_button), FALSE); - if (!box->imhtml_visible) + if (!box->webview_visible) g_signal_emit_by_name(G_OBJECT(box), "changed", NULL, NULL); return TRUE; } @@ -1703,14 +1706,13 @@ } static void -imhtml_cursor_moved_cb(gpointer data, GtkMovementStep step, gint count, gboolean extend, - GtkWidget *widget) +webview_cursor_moved_cb(gpointer data, GtkWebView *webview) { /* Restart the typing timeout if arrow keys are pressed while editing the message */ PidginStatusBox *status_box = data; if (status_box->typing == 0) return; - imhtml_changed_cb(NULL, status_box); + webview_changed_cb(NULL, status_box); } static void @@ -1761,12 +1763,11 @@ GtkCellRenderer *text_rend; GtkCellRenderer *icon_rend; GtkCellRenderer *emblem_rend; - GtkTextBuffer *buffer; GtkWidget *toplevel; GtkTreeSelection *sel; gtk_widget_set_has_window(GTK_WIDGET(status_box), FALSE); - status_box->imhtml_visible = FALSE; + status_box->webview_visible = FALSE; status_box->network_available = purple_network_is_available(); status_box->connecting = FALSE; status_box->typing = 0; @@ -1859,10 +1860,9 @@ g_object_set(status_box->text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); status_box->vbox = gtk_vbox_new(0, FALSE); - status_box->sw = pidgin_create_imhtml(FALSE, &status_box->imhtml, NULL, NULL); - gtk_imhtml_set_editable(GTK_IMHTML(status_box->imhtml), TRUE); - - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml)); + status_box->sw = pidgin_create_webview(FALSE, &status_box->webview, NULL, NULL); + gtk_webview_set_editable(GTK_WEBVIEW(status_box->webview), TRUE); + #if 0 g_signal_connect(G_OBJECT(status_box->toggle_button), "button-press-event", G_CALLBACK(button_pressed_cb), status_box); @@ -1873,19 +1873,17 @@ G_CALLBACK(toggle_key_press_cb), status_box); g_signal_connect(G_OBJECT(status_box->toggle_button), "button-press-event", G_CALLBACK(toggled_cb), status_box); - g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(imhtml_changed_cb), status_box); - g_signal_connect(G_OBJECT(status_box->imhtml), "format_function_toggle", - G_CALLBACK(imhtml_format_changed_cb), status_box); - g_signal_connect_swapped(G_OBJECT(status_box->imhtml), "move_cursor", - G_CALLBACK(imhtml_cursor_moved_cb), status_box); - g_signal_connect(G_OBJECT(status_box->imhtml), "key_press_event", - G_CALLBACK(imhtml_remove_focus), status_box); - g_signal_connect_swapped(G_OBJECT(status_box->imhtml), "message_send", G_CALLBACK(remove_typing_cb), status_box); - -#ifdef USE_GTKSPELL + g_signal_connect(G_OBJECT(status_box->webview), "changed", + G_CALLBACK(webview_changed_cb), status_box); + g_signal_connect(G_OBJECT(status_box->webview), "format-toggled", + G_CALLBACK(webview_format_changed_cb), status_box); + g_signal_connect_swapped(G_OBJECT(status_box->webview), "selection-changed", + G_CALLBACK(webview_cursor_moved_cb), status_box); + g_signal_connect(G_OBJECT(status_box->webview), "key-press-event", + G_CALLBACK(webview_remove_focus), status_box); + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck")) - pidgin_setup_gtkspell(GTK_TEXT_VIEW(status_box->imhtml)); -#endif + pidgin_webview_set_spellcheck(GTK_WEBVIEW(status_box->webview), TRUE); gtk_widget_set_parent(status_box->vbox, GTK_WIDGET(status_box)); gtk_widget_show_all(status_box->vbox); @@ -1893,9 +1891,9 @@ gtk_box_pack_start(GTK_BOX(status_box->vbox), status_box->sw, TRUE, TRUE, 0); - g_signal_connect(G_OBJECT(status_box), "scroll_event", G_CALLBACK(combo_box_scroll_event_cb), NULL); - g_signal_connect(G_OBJECT(status_box->imhtml), "scroll_event", - G_CALLBACK(imhtml_scroll_event_cb), status_box->imhtml); + g_signal_connect(G_OBJECT(status_box), "scroll-event", G_CALLBACK(combo_box_scroll_event_cb), NULL); + g_signal_connect(G_OBJECT(status_box->webview), "scroll-event", + G_CALLBACK(webview_scroll_event_cb), status_box->webview); g_signal_connect(G_OBJECT(status_box->popup_window), "button_release_event", G_CALLBACK(treeview_button_release_cb), status_box); g_signal_connect(G_OBJECT(status_box->popup_window), "key_press_event", G_CALLBACK(treeview_key_press_event), status_box); g_signal_connect(G_OBJECT(status_box->tree_view), "cursor-changed", @@ -1954,8 +1952,8 @@ *minimum_height = MAX(*minimum_height, 34) + border_width * 2; *natural_height = MAX(*natural_height, 34) + border_width * 2; - /* If the gtkimhtml is visible, then add some additional padding */ - if (PIDGIN_STATUS_BOX(widget)->imhtml_visible) { + /* If the gtkwebview is visible, then add some additional padding */ + if (PIDGIN_STATUS_BOX(widget)->webview_visible) { gtk_widget_get_preferred_height(PIDGIN_STATUS_BOX(widget)->vbox, &box_min_height, &box_nat_height); @@ -1980,8 +1978,8 @@ requisition->height = MAX(requisition->height, 34); requisition->height += border_width * 2; - /* If the gtkimhtml is visible, then add some additional padding */ - if (PIDGIN_STATUS_BOX(widget)->imhtml_visible) { + /* If the gtkwebview is visible, then add some additional padding */ + if (PIDGIN_STATUS_BOX(widget)->webview_visible) { gtk_widget_size_request(PIDGIN_STATUS_BOX(widget)->vbox, &box_req); if (box_req.height > 1) requisition->height += box_req.height + border_width * 2; @@ -2419,7 +2417,7 @@ if (!message || !*message) { gtk_widget_hide(status_box->vbox); - status_box->imhtml_visible = FALSE; + status_box->webview_visible = FALSE; if (message != NULL) { g_free(message); @@ -2577,6 +2575,8 @@ static void update_size(PidginStatusBox *status_box) { +#if 0 + /* TODO WebKit Sizing */ GtkTextBuffer *buffer; GtkTextIter iter; int display_lines; @@ -2586,25 +2586,28 @@ int pad_top, pad_inside, pad_bottom; gboolean interior_focus; int focus_width; - - if (!status_box->imhtml_visible) +#endif + + if (!status_box->webview_visible) { if (status_box->vbox != NULL) gtk_widget_set_size_request(status_box->vbox, -1, -1); return; } - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml)); +#if 0 + /* TODO WebKit: Entry sizing */ + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->webview)); height = 0; display_lines = 1; gtk_text_buffer_get_start_iter(buffer, &iter); do { - gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline); + gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->webview), &iter, &oneline); height += oneline.height; display_lines++; } while (display_lines <= 4 && - gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->imhtml), &iter)); + gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->webview), &iter)); /* * This check fixes the case where the last character entered is a @@ -2618,7 +2621,7 @@ && gtk_text_iter_backward_char(&iter) && gtk_text_iter_get_char(&iter) == '\n') { - gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline); + gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->webview), &iter, &oneline); height += oneline.height; display_lines++; } @@ -2629,14 +2632,14 @@ lines = MIN(lines, 4); display_lines = MIN(display_lines, 4); - pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(status_box->imhtml)); - pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(status_box->imhtml)); - pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(status_box->imhtml)); + pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(status_box->webview)); + pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(status_box->webview)); + pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(status_box->webview)); height += (pad_top + pad_bottom) * lines; height += (pad_inside) * (display_lines - lines); - gtk_widget_style_get(status_box->imhtml, + gtk_widget_style_get(status_box->webview, "interior-focus", &interior_focus, "focus-line-width", &focus_width, NULL); @@ -2644,6 +2647,8 @@ height += 2 * focus_width; gtk_widget_set_size_request(status_box->vbox, -1, height + PIDGIN_HIG_BOX_SPACE); +#endif + gtk_widget_set_size_request(status_box->vbox, -1, -1); } static void remove_typing_cb(PidginStatusBox *status_box) @@ -2655,8 +2660,11 @@ return; } - gtk_imhtml_set_populate_primary_clipboard( - GTK_IMHTML(status_box->imhtml), TRUE); +#if 0 + /* TODO WebKit: Doesn't do this? */ + gtk_webview_set_populate_primary_clipboard( + GTK_WEBVIEW(status_box->webview), TRUE); +#endif purple_timeout_remove(status_box->typing); status_box->typing = 0; @@ -2735,7 +2743,7 @@ accounts = g_list_prepend(accounts, status_box->account); else accounts = purple_accounts_get_all_active(); - status_box->imhtml_visible = FALSE; + status_box->webview_visible = FALSE; for (node = accounts; node != NULL; node = node->next) { PurpleAccount *account; @@ -2746,7 +2754,7 @@ if ((status_type != NULL) && (purple_status_type_get_attr(status_type, "message") != NULL)) { - status_box->imhtml_visible = TRUE; + status_box->webview_visible = TRUE; break; } } @@ -2754,21 +2762,18 @@ if (gtk_widget_get_sensitive(GTK_WIDGET(status_box))) { - if (status_box->imhtml_visible) + if (status_box->webview_visible) { - GtkTextIter start, end; - GtkTextBuffer *buffer; gtk_widget_show_all(status_box->vbox); status_box->typing = purple_timeout_add_seconds(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box); - gtk_widget_grab_focus(status_box->imhtml); - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml)); - - gtk_imhtml_set_populate_primary_clipboard( - GTK_IMHTML(status_box->imhtml), FALSE); - - gtk_text_buffer_get_bounds(buffer, &start, &end); - gtk_text_buffer_move_mark(buffer, gtk_text_buffer_get_mark(buffer, "insert"), &end); - gtk_text_buffer_move_mark(buffer, gtk_text_buffer_get_mark(buffer, "selection_bound"), &start); + gtk_widget_grab_focus(status_box->webview); +#if 0 + /* TODO WebKit: Doesn't do this? */ + gtk_webview_set_populate_primary_clipboard( + GTK_WEBVIEW(status_box->webview), FALSE); +#endif + + webkit_web_view_select_all(WEBKIT_WEB_VIEW(status_box->webview)); } else { @@ -2805,7 +2810,8 @@ return index; } -static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data) +static void +webview_changed_cb(GtkWebView *webview, void *data) { PidginStatusBox *status_box = (PidginStatusBox*)data; if (gtk_widget_get_sensitive(GTK_WIDGET(status_box))) @@ -2819,15 +2825,16 @@ pidgin_status_box_refresh(status_box); } -static void imhtml_format_changed_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *data) +static void +webview_format_changed_cb(GtkWebView *webview, GtkWebViewButtons buttons, void *data) { - imhtml_changed_cb(NULL, data); + webview_changed_cb(NULL, data); } char *pidgin_status_box_get_message(PidginStatusBox *status_box) { - if (status_box->imhtml_visible) - return gtk_imhtml_get_markup(GTK_IMHTML(status_box->imhtml)); + if (status_box->webview_visible) + return gtk_webview_get_body_html(GTK_WEBVIEW(status_box->webview)); else return NULL; }
--- a/pidgin/gtkstatusbox.h Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkstatusbox.h Tue Aug 14 22:26:33 2012 +0200 @@ -29,7 +29,7 @@ #define __PIDGIN_STATUS_BOX_H__ #include <gtk/gtk.h> -#include "gtkimhtml.h" +#include "gtkwebview.h" #include "account.h" #include "imgstore.h" #include "savedstatuses.h" @@ -87,7 +87,7 @@ PurpleAccount *token_status_account; GtkWidget *vbox, *sw; - GtkWidget *imhtml; + GtkWidget *webview; PurpleStoredImage *buddy_icon_img; GdkPixbuf *buddy_icon; @@ -101,7 +101,7 @@ int icon_size; gboolean icon_opaque; - gboolean imhtml_visible; + gboolean webview_visible; GtkWidget *cell_view; GtkCellRenderer *icon_rend;
--- a/pidgin/gtkthemes.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkthemes.c Tue Aug 14 22:26:33 2012 +0200 @@ -30,9 +30,9 @@ #include "gtkconv.h" #include "gtkdialogs.h" -#include "gtkimhtml.h" #include "gtksmiley.h" #include "gtkthemes.h" +#include "gtkwebview.h" GSList *smiley_themes = NULL; struct smiley_theme *current_smiley_theme; @@ -120,21 +120,19 @@ g_free(theme_dir); } -static void _pidgin_themes_smiley_themeize(GtkWidget *imhtml, gboolean custom) +static void _pidgin_themes_smiley_themeize(GtkWidget *webview, gboolean custom) { - /* TODO WEBKIT: move imhtml dependency to use webview. */ -#if 0 struct smiley_list *list; if (!current_smiley_theme) return; - gtk_imhtml_remove_smileys(GTK_IMHTML(imhtml)); + gtk_webview_remove_smileys(GTK_WEBVIEW(webview)); list = current_smiley_theme->list; while (list) { char *sml = !strcmp(list->sml, "default") ? NULL : list->sml; GSList *icons = list->smileys; while (icons) { - gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data); + gtk_webview_associate_smiley(GTK_WEBVIEW(webview), sml, icons->data); icons = icons->next; } @@ -142,50 +140,36 @@ icons = pidgin_smileys_get_all(); while (icons) { - gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data); + gtk_webview_associate_smiley(GTK_WEBVIEW(webview), sml, icons->data); icons = icons->next; } } list = list->next; } -#endif /* if 0 */ } -void pidgin_themes_smiley_themeize(GtkWidget *imhtml) +void +pidgin_themes_smiley_themeize(GtkWidget *webview) { - _pidgin_themes_smiley_themeize(imhtml, FALSE); + _pidgin_themes_smiley_themeize(webview, FALSE); } -void pidgin_themes_smiley_themeize_custom(GtkWidget *imhtml) +void +pidgin_themes_smiley_themeize_custom(GtkWidget *webview) { - _pidgin_themes_smiley_themeize(imhtml, TRUE); + _pidgin_themes_smiley_themeize(webview, TRUE); } static void pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme) { - GHashTable *already_freed; struct smiley_list *wer; - already_freed = g_hash_table_new(g_direct_hash, g_direct_equal); for (wer = theme->list; wer != NULL; wer = theme->list) { while (wer->smileys) { - GtkIMHtmlSmiley *uio = wer->smileys->data; - - if (uio->imhtml) { - g_signal_handlers_disconnect_matched(uio->imhtml, G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, uio); - } - - if (uio->icon) - g_object_unref(uio->icon); - if (g_hash_table_lookup(already_freed, uio->file) == NULL) { - g_free(uio->file); - g_hash_table_insert(already_freed, uio->file, GINT_TO_POINTER(1)); - } - g_free(uio->smile); - g_free(uio); + GtkWebViewSmiley *uio = wer->smileys->data; + gtk_webview_smiley_destroy(uio); wer->smileys = g_slist_delete_link(wer->smileys, wer->smileys); } theme->list = wer->next; @@ -193,8 +177,6 @@ g_free(wer); } theme->list = NULL; - - g_hash_table_destroy(already_freed); } static void @@ -323,7 +305,7 @@ if (!sfile) { sfile = g_build_filename(dirname, l, NULL); } else { - GtkIMHtmlSmiley *smiley = gtk_imhtml_smiley_create(sfile, l, hidden, 0); + GtkWebViewSmiley *smiley = gtk_webview_smiley_create(sfile, l, hidden, 0); list->smileys = g_slist_prepend(list->smileys, smiley); g_hash_table_insert (list->files, g_strdup(l), g_strdup(sfile)); }
--- a/pidgin/gtkwebview.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkwebview.c Tue Aug 14 22:26:33 2012 +0200 @@ -53,6 +53,7 @@ CLEAR_FORMAT, UPDATE_FORMAT, CHANGED, + HTML_APPENDED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -62,6 +63,21 @@ *****************************************************************************/ typedef struct { + WebKitWebInspector *inspector; + WebKitDOMNode *node; +} GtkWebViewInspectData; + +typedef struct { + WebKitWebView *webview; + gunichar ch; +} GtkWebViewInsertData; + +typedef struct { + const char *label; + gunichar ch; +} GtkUnicodeMenuEntry; + +typedef struct { char *name; int length; @@ -69,6 +85,26 @@ gboolean (*context_menu)(GtkWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu); } GtkWebViewProtocol; +struct _GtkWebViewSmiley { + gchar *smile; + gchar *file; + GdkPixbufAnimation *icon; + gboolean hidden; + GdkPixbufLoader *loader; + GSList *anchors; + GtkWebViewSmileyFlags flags; + GtkWebView *webview; + gpointer data; + gsize datasize; +}; + +typedef struct _GtkSmileyTree GtkSmileyTree; +struct _GtkSmileyTree { + GString *values; + GtkSmileyTree **children; + GtkWebViewSmiley *image; +}; + typedef struct _GtkWebViewPriv { /* Processing queues */ gboolean is_loading; @@ -77,6 +113,7 @@ /* Scroll adjustments */ GtkAdjustment *vadj; + gboolean autoscroll; guint scroll_src; GTimer *scroll_time; @@ -87,6 +124,10 @@ gboolean block_changed:1; } edit; + /* Smileys */ + char *protocol_name; + GHashTable *smiley_data; + GtkSmileyTree *default_smilies; } GtkWebViewPriv; /****************************************************************************** @@ -96,6 +137,531 @@ static WebKitWebViewClass *parent_class = NULL; /****************************************************************************** + * Smileys + *****************************************************************************/ + +const char * +gtk_webview_get_protocol_name(GtkWebView *webview) +{ + GtkWebViewPriv *priv; + + g_return_val_if_fail(webview != NULL, NULL); + + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + return priv->protocol_name; +} + +void +gtk_webview_set_protocol_name(GtkWebView *webview, const char *protocol_name) +{ + GtkWebViewPriv *priv; + + g_return_if_fail(webview != NULL); + + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + priv->protocol_name = g_strdup(protocol_name); +} + +static GtkSmileyTree * +gtk_smiley_tree_new(void) +{ + return g_new0(GtkSmileyTree, 1); +} + +static void +gtk_smiley_tree_insert(GtkSmileyTree *tree, GtkWebViewSmiley *smiley) +{ + GtkSmileyTree *t = tree; + const char *x = smiley->smile; + + if (!(*x)) + return; + + do { + char *pos; + gsize index; + + if (!t->values) + t->values = g_string_new(""); + + pos = strchr(t->values->str, *x); + if (!pos) { + t->values = g_string_append_c(t->values, *x); + index = t->values->len - 1; + t->children = g_realloc(t->children, t->values->len * sizeof(GtkSmileyTree *)); + t->children[index] = g_new0(GtkSmileyTree, 1); + } else + index = pos - t->values->str; + + t = t->children[index]; + + x++; + } while (*x); + + t->image = smiley; +} + +static void +gtk_smiley_tree_destroy(GtkSmileyTree *tree) +{ + GSList *list = g_slist_prepend(NULL, tree); + + while (list) { + GtkSmileyTree *t = list->data; + gsize i; + list = g_slist_delete_link(list, list); + if (t && t->values) { + for (i = 0; i < t->values->len; i++) + list = g_slist_prepend(list, t->children[i]); + g_string_free(t->values, TRUE); + g_free(t->children); + } + + g_free(t); + } +} + +static void +gtk_smiley_tree_remove(GtkSmileyTree *tree, GtkWebViewSmiley *smiley) +{ + GtkSmileyTree *t = tree; + const gchar *x = smiley->smile; + int len = 0; + + while (*x) { + char *pos; + + if (!t->values) + return; + + pos = strchr(t->values->str, *x); + if (pos) + t = t->children[pos - t->values->str]; + else + return; + + x++; len++; + } + + t->image = NULL; +} + +static int +gtk_smiley_tree_lookup(GtkSmileyTree *tree, const char *text) +{ + GtkSmileyTree *t = tree; + const char *x = text; + int len = 0; + const char *amp; + int alen; + + while (*x) { + char *pos; + + if (!t->values) + break; + + if (*x == '&' && (amp = purple_markup_unescape_entity(x, &alen))) { + gboolean matched = TRUE; + /* Make sure all chars of the unescaped value match */ + while (*(amp + 1)) { + pos = strchr(t->values->str, *amp); + if (pos) + t = t->children[pos - t->values->str]; + else { + matched = FALSE; + break; + } + amp++; + } + if (!matched) + break; + + pos = strchr(t->values->str, *amp); + } + else if (*x == '<') /* Because we're all WYSIWYG now, a '<' char should + * only appear as the start of a tag. Perhaps a + * safer (but costlier) check would be to call + * gtk_imhtml_is_tag on it */ + break; + else { + alen = 1; + pos = strchr(t->values->str, *x); + } + + if (pos) + t = t->children[pos - t->values->str]; + else + break; + + x += alen; + len += alen; + } + + if (t->image) + return len; + + return 0; +} + +static void +gtk_webview_disassociate_smiley_foreach(gpointer key, gpointer value, + gpointer user_data) +{ + GtkSmileyTree *tree = (GtkSmileyTree *)value; + GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)user_data; + gtk_smiley_tree_remove(tree, smiley); +} + +static void +gtk_webview_disconnect_smiley(GtkWebView *webview, GtkWebViewSmiley *smiley) +{ + smiley->webview = NULL; + g_signal_handlers_disconnect_matched(webview, G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, smiley); +} + +static void +gtk_webview_disassociate_smiley(GtkWebViewSmiley *smiley) +{ + if (smiley->webview) { + GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(smiley->webview); + gtk_smiley_tree_remove(priv->default_smilies, smiley); + g_hash_table_foreach(priv->smiley_data, + gtk_webview_disassociate_smiley_foreach, smiley); + g_signal_handlers_disconnect_matched(smiley->webview, + G_SIGNAL_MATCH_DATA, 0, 0, NULL, + NULL, smiley); + smiley->webview = NULL; + } +} + +void +gtk_webview_associate_smiley(GtkWebView *webview, const char *sml, + GtkWebViewSmiley *smiley) +{ + GtkSmileyTree *tree; + GtkWebViewPriv *priv; + + g_return_if_fail(webview != NULL); + g_return_if_fail(GTK_IS_WEBVIEW(webview)); + + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + + if (sml == NULL) + tree = priv->default_smilies; + else if (!(tree = g_hash_table_lookup(priv->smiley_data, sml))) { + tree = gtk_smiley_tree_new(); + g_hash_table_insert(priv->smiley_data, g_strdup(sml), tree); + } + + /* need to disconnect old webview, if there is one */ + if (smiley->webview) { + g_signal_handlers_disconnect_matched(smiley->webview, + G_SIGNAL_MATCH_DATA, 0, 0, NULL, + NULL, smiley); + } + + smiley->webview = webview; + + gtk_smiley_tree_insert(tree, smiley); + + /* connect destroy signal for the webview */ + g_signal_connect(webview, "destroy", + G_CALLBACK(gtk_webview_disconnect_smiley), smiley); +} + +static gboolean +gtk_webview_is_smiley(GtkWebViewPriv *priv, const char *sml, const char *text, + int *len) +{ + GtkSmileyTree *tree; + + if (!sml) + sml = priv->protocol_name; + + if (!sml || !(tree = g_hash_table_lookup(priv->smiley_data, sml))) + tree = priv->default_smilies; + + if (tree == NULL) + return FALSE; + + *len = gtk_smiley_tree_lookup(tree, text); + return (*len > 0); +} + +static GtkWebViewSmiley * +gtk_webview_smiley_get_from_tree(GtkSmileyTree *t, const char *text) +{ + const char *x = text; + char *pos; + + if (t == NULL) + return NULL; + + while (*x) { + if (!t->values) + return NULL; + + pos = strchr(t->values->str, *x); + if (!pos) + return NULL; + + t = t->children[pos - t->values->str]; + x++; + } + + return t->image; +} + +GtkWebViewSmiley * +gtk_webview_smiley_find(GtkWebView *webview, const char *sml, const char *text) +{ + GtkWebViewPriv *priv; + GtkWebViewSmiley *ret; + + g_return_val_if_fail(webview != NULL, NULL); + + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + + /* Look for custom smileys first */ + if (sml != NULL) { + ret = gtk_webview_smiley_get_from_tree(g_hash_table_lookup(priv->smiley_data, sml), text); + if (ret != NULL) + return ret; + } + + /* Fall back to check for default smileys */ + return gtk_webview_smiley_get_from_tree(priv->default_smilies, text); +} + +static GdkPixbufAnimation * +gtk_smiley_get_image(GtkWebViewSmiley *smiley) +{ + if (!smiley->icon) { + if (smiley->file) { + smiley->icon = gdk_pixbuf_animation_new_from_file(smiley->file, NULL); + } else if (smiley->loader) { + smiley->icon = gdk_pixbuf_loader_get_animation(smiley->loader); + if (smiley->icon) + g_object_ref(G_OBJECT(smiley->icon)); + } + } + + return smiley->icon; +} + +static void +gtk_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data) +{ + GtkWebViewSmiley *smiley; + + smiley = (GtkWebViewSmiley *)user_data; + smiley->icon = gdk_pixbuf_loader_get_animation(loader); + + if (smiley->icon) + g_object_ref(G_OBJECT(smiley->icon)); +} + +static void +gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data) +{ + GtkWebViewSmiley *smiley; + GtkWidget *icon = NULL; + GtkTextChildAnchor *anchor = NULL; + GSList *current = NULL; + + smiley = (GtkWebViewSmiley *)user_data; + if (!smiley->webview) { + g_object_unref(G_OBJECT(loader)); + smiley->loader = NULL; + return; + } + + for (current = smiley->anchors; current; current = g_slist_next(current)) { + anchor = GTK_TEXT_CHILD_ANCHOR(current->data); + if (gtk_text_child_anchor_get_deleted(anchor)) + icon = NULL; + else + icon = gtk_image_new_from_animation(smiley->icon); + + if (icon) { + GList *wids; + gtk_widget_show(icon); + + wids = gtk_text_child_anchor_get_widgets(anchor); + + g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", + purple_unescape_html(smiley->smile), g_free); + g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", + g_strdup(smiley->smile), g_free); + + if (smiley->webview) { + if (wids) { + GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data)); + g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL); + g_list_free(children); + gtk_container_add(GTK_CONTAINER(wids->data), icon); + } else + gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->webview), icon, anchor); + } + g_list_free(wids); + } + g_object_unref(anchor); + } + + g_slist_free(smiley->anchors); + smiley->anchors = NULL; + + g_object_unref(G_OBJECT(loader)); + smiley->loader = NULL; +} + +static void +gtk_custom_smiley_size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer data) +{ + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys")) { + int custom_smileys_size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size"); + if (width <= custom_smileys_size && height <= custom_smileys_size) + return; + + if (width >= height) { + height = height * custom_smileys_size / width; + width = custom_smileys_size; + } else { + width = width * custom_smileys_size / height; + height = custom_smileys_size; + } + } + gdk_pixbuf_loader_set_size(loader, width, height); +} + +GtkWebViewSmiley * +gtk_webview_smiley_create(const char *file, const char *shortcut, gboolean hide, + GtkWebViewSmileyFlags flags) +{ + GtkWebViewSmiley *smiley = g_new0(GtkWebViewSmiley, 1); + smiley->file = g_strdup(file); + smiley->smile = g_strdup(shortcut); + smiley->hidden = hide; + smiley->flags = flags; + smiley->webview = NULL; + gtk_webview_smiley_reload(smiley); + return smiley; +} + +void +gtk_webview_smiley_reload(GtkWebViewSmiley *smiley) +{ + if (smiley->icon) + g_object_unref(smiley->icon); + if (smiley->loader) + g_object_unref(smiley->loader); + + smiley->icon = NULL; + smiley->loader = NULL; + + if (smiley->file) { + /* We do not use the pixbuf loader for a smiley that can be loaded + * from a file. (e.g., local custom smileys) + */ + return; + } + + smiley->loader = gdk_pixbuf_loader_new(); + + g_signal_connect(smiley->loader, "area_prepared", + G_CALLBACK(gtk_custom_smiley_allocated), smiley); + g_signal_connect(smiley->loader, "closed", + G_CALLBACK(gtk_custom_smiley_closed), smiley); + g_signal_connect(smiley->loader, "size_prepared", + G_CALLBACK(gtk_custom_smiley_size_prepared), smiley); +} + +const char * +gtk_webview_smiley_get_smile(const GtkWebViewSmiley *smiley) +{ + return smiley->smile; +} + +const char * +gtk_webview_smiley_get_file(const GtkWebViewSmiley *smiley) +{ + return smiley->file; +} + +gboolean +gtk_webview_smiley_get_hidden(const GtkWebViewSmiley *smiley) +{ + return smiley->hidden; +} + +GtkWebViewSmileyFlags +gtk_webview_smiley_get_flags(const GtkWebViewSmiley *smiley) +{ + return smiley->flags; +} + +void +gtk_webview_smiley_destroy(GtkWebViewSmiley *smiley) +{ + gtk_webview_disassociate_smiley(smiley); + g_free(smiley->smile); + g_free(smiley->file); + if (smiley->icon) + g_object_unref(smiley->icon); + if (smiley->loader) + g_object_unref(smiley->loader); + g_free(smiley->data); + g_free(smiley); +} + +void +gtk_webview_remove_smileys(GtkWebView *webview) +{ + GtkWebViewPriv *priv; + + g_return_if_fail(webview != NULL); + + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + + g_hash_table_destroy(priv->smiley_data); + gtk_smiley_tree_destroy(priv->default_smilies); + priv->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gtk_smiley_tree_destroy); + priv->default_smilies = gtk_smiley_tree_new(); +} + +void +gtk_webview_insert_smiley(GtkWebView *webview, const char *sml, + const char *smiley) +{ + GtkWebViewPriv *priv; + char *unescaped; + GtkWebViewSmiley *webview_smiley; + + g_return_if_fail(webview != NULL); + + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + + unescaped = purple_unescape_html(smiley); + webview_smiley = gtk_webview_smiley_find(webview, sml, unescaped); + + if (priv->format_functions & GTK_WEBVIEW_SMILEY) { + char *tmp; + /* TODO Better smiley insertion... */ + tmp = g_strdup_printf("<img isEmoticon src='purple-smiley:%p' alt='%s'>", + webview_smiley, smiley); + gtk_webview_append_html(webview, tmp); + g_free(tmp); + } else { + gtk_webview_append_html(webview, smiley); + } + + g_free(unescaped); +} + +/****************************************************************************** * Helpers *****************************************************************************/ @@ -147,6 +713,9 @@ char *str; WebKitDOMDocument *doc; WebKitDOMHTMLElement *body; + WebKitDOMNode *start, *end; + WebKitDOMRange *range; + gboolean require_scroll = FALSE; if (priv->is_loading) { priv->loader = 0; @@ -164,8 +733,43 @@ case LOAD_HTML: doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); body = webkit_dom_document_get_body(doc); + start = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body)); + + if (priv->autoscroll) { + require_scroll = (gtk_adjustment_get_value(priv->vadj) + >= (gtk_adjustment_get_upper(priv->vadj) - + 1.5*gtk_adjustment_get_page_size(priv->vadj))); + } + webkit_dom_html_element_insert_adjacent_html(body, "beforeend", str, NULL); + + range = webkit_dom_document_create_range(doc); + if (start) { + end = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body)); + webkit_dom_range_set_start_after(range, + WEBKIT_DOM_NODE(start), + NULL); + webkit_dom_range_set_end_after(range, + WEBKIT_DOM_NODE(end), + NULL); + } else { + webkit_dom_range_select_node_contents(range, + WEBKIT_DOM_NODE(body), + NULL); + } + + if (require_scroll) { + if (start) + webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(start), + TRUE); + else + webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(body), + TRUE); + } + + g_signal_emit(webview, signals[HTML_APPENDED], 0, range); + break; case LOAD_JS: @@ -204,6 +808,12 @@ priv->loader = g_idle_add((GSourceFunc)process_load_queue, webview); } +static void +webview_show_inspector_cb(GtkWidget *item, GtkWebViewInspectData *data) +{ + webkit_web_inspector_inspect_node(data->inspector, data->node); +} + static GtkWebViewProtocol * webview_find_protocol(const char *url, gboolean reverse) { @@ -254,12 +864,113 @@ return TRUE; } +static GtkWidget * +get_input_methods_menu(WebKitWebView *webview) +{ + GtkSettings *settings; + gboolean show = TRUE; + GtkWidget *item; + GtkWidget *menu; + GtkIMContext *im; + + settings = webview ? gtk_widget_get_settings(GTK_WIDGET(webview)) : gtk_settings_get_default(); + + if (settings) + g_object_get(settings, "gtk-show-input-method-menu", &show, NULL); + if (!show) + return NULL; + + item = gtk_image_menu_item_new_with_mnemonic(_("Input _Methods")); + + g_object_get(webview, "im-context", &im, NULL); + menu = gtk_menu_new(); + gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(im), + GTK_MENU_SHELL(menu)); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); + + return item; +} + +/* Values taken from gtktextutil.c */ +static const GtkUnicodeMenuEntry bidi_menu_entries[] = { + { N_("LRM _Left-to-right mark"), 0x200E }, + { N_("RLM _Right-to-left mark"), 0x200F }, + { N_("LRE Left-to-right _embedding"), 0x202A }, + { N_("RLE Right-to-left e_mbedding"), 0x202B }, + { N_("LRO Left-to-right _override"), 0x202D }, + { N_("RLO Right-to-left o_verride"), 0x202E }, + { N_("PDF _Pop directional formatting"), 0x202C }, + { N_("ZWS _Zero width space"), 0x200B }, + { N_("ZWJ Zero width _joiner"), 0x200D }, + { N_("ZWNJ Zero width _non-joiner"), 0x200C } +}; + +static void +insert_control_character_cb(GtkMenuItem *item, GtkWebViewInsertData *data) +{ + WebKitWebView *webview = data->webview; + gunichar ch = data->ch; + GtkWebViewPriv *priv; + WebKitDOMDocument *dom; + char buf[6]; + + priv = GTK_WEBVIEW_GET_PRIVATE(GTK_WEBVIEW(webview)); + dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); + + g_unichar_to_utf8(ch, buf); + priv->edit.block_changed = TRUE; + webkit_dom_document_exec_command(dom, "insertHTML", FALSE, buf); + priv->edit.block_changed = FALSE; +} + +static GtkWidget * +get_unicode_menu(WebKitWebView *webview) +{ + GtkSettings *settings; + gboolean show = TRUE; + GtkWidget *menuitem; + GtkWidget *menu; + int i; + + settings = webview ? gtk_widget_get_settings(GTK_WIDGET(webview)) : gtk_settings_get_default(); + + if (settings) + g_object_get(settings, "gtk-show-unicode-menu", &show, NULL); + if (!show) + return NULL; + + menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Insert Unicode Control Character")); + + menu = gtk_menu_new(); + for (i = 0; i < G_N_ELEMENTS(bidi_menu_entries); i++) { + GtkWebViewInsertData *data; + GtkWidget *item; + + data = g_new0(GtkWebViewInsertData, 1); + data->webview = webview; + data->ch = bidi_menu_entries[i].ch; + + item = gtk_menu_item_new_with_mnemonic(_(bidi_menu_entries[i].label)); + g_signal_connect_data(item, "activate", + G_CALLBACK(insert_control_character_cb), data, + (GClosureNotify)g_free, 0); + gtk_widget_show(item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); + + return menuitem; +} + static void do_popup_menu(WebKitWebView *webview, int button, int time, int context, - WebKitDOMHTMLAnchorElement *link, const char *uri) + WebKitDOMNode *node, const char *uri) { GtkWidget *menu; GtkWidget *cut, *copy, *paste, *delete, *select; + WebKitWebSettings *settings; + gboolean inspector; menu = gtk_menu_new(); g_signal_connect(menu, "selection-done", @@ -270,11 +981,16 @@ GtkWebViewProtocol *proto = NULL; GList *children; - if (uri && link) + while (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(node)) { + node = webkit_dom_node_get_parent_node(node); + } + + if (uri && node) proto = webview_find_protocol(uri, FALSE); if (proto && proto->context_menu) { - proto->context_menu(GTK_WEBVIEW(webview), link, menu); + proto->context_menu(GTK_WEBVIEW(webview), + WEBKIT_DOM_HTML_ANCHOR_ELEMENT(node), menu); } children = gtk_container_get_children(GTK_CONTAINER(menu)); @@ -333,6 +1049,43 @@ webkit_web_view_can_cut_clipboard(webview)); } + settings = webkit_web_view_get_settings(webview); + g_object_get(G_OBJECT(settings), "enable-developer-extras", &inspector, NULL); + if (inspector) { + GtkWidget *inspect; + GtkWebViewInspectData *data; + + data = g_new0(GtkWebViewInspectData, 1); + data->inspector = webkit_web_view_get_inspector(webview); + data->node = node; + + pidgin_separator(menu); + + inspect = pidgin_new_item_from_stock(menu, _("Inspect _Element"), NULL, + NULL, NULL, 0, 0, NULL); + g_signal_connect_data(G_OBJECT(inspect), "activate", + G_CALLBACK(webview_show_inspector_cb), + data, (GClosureNotify)g_free, 0); + } + + if (webkit_web_view_get_editable(webview)) { + GtkWidget *im = get_input_methods_menu(webview); + GtkWidget *unicode = get_unicode_menu(webview); + + if (im || unicode) + pidgin_separator(menu); + + if (im) { + gtk_menu_shell_append(GTK_MENU_SHELL(menu), im); + gtk_widget_show(im); + } + + if (unicode) { + gtk_menu_shell_append(GTK_MENU_SHELL(menu), unicode); + gtk_widget_show(unicode); + } + } + g_signal_emit_by_name(G_OBJECT(webview), "populate-popup", menu); gtk_menu_attach_to_widget(GTK_MENU(menu), GTK_WIDGET(webview), NULL); @@ -342,9 +1095,31 @@ static gboolean webview_popup_menu(WebKitWebView *webview) { + WebKitDOMDocument *doc; + WebKitDOMElement *active; + WebKitDOMElement *link; + int context; + char *uri; + + context = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT; + uri = NULL; + + doc = webkit_web_view_get_dom_document(webview); + active = webkit_dom_html_document_get_active_element(WEBKIT_DOM_HTML_DOCUMENT(doc)); + + link = active; + while (link && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link)) + link = webkit_dom_node_get_parent_element(WEBKIT_DOM_NODE(link)); + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link)) { + context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK; + uri = webkit_dom_html_anchor_element_get_href(WEBKIT_DOM_HTML_ANCHOR_ELEMENT(link)); + } + do_popup_menu(webview, 0, gtk_get_current_event_time(), - WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT, - NULL, NULL); + context, WEBKIT_DOM_NODE(active), uri); + + g_free(uri); + return TRUE; } @@ -364,14 +1139,8 @@ "link-uri", &uri, NULL); - if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) { - while (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(node)) { - node = webkit_dom_node_get_parent_node(node); - } - } - do_popup_menu(webview, event->button, event->time, context, - WEBKIT_DOM_HTML_ANCHOR_ELEMENT(node), uri); + node, uri); g_free(uri); @@ -577,6 +1346,10 @@ } g_queue_free(priv->load_queue); + g_hash_table_destroy(priv->smiley_data); + gtk_smiley_tree_destroy(priv->default_smilies); + g_free(priv->protocol_name); + G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview)); } @@ -621,6 +1394,14 @@ G_STRUCT_OFFSET(GtkWebViewClass, changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + signals[HTML_APPENDED] = g_signal_new("html-appended", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(GtkWebViewClass, html_appended), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, WEBKIT_TYPE_DOM_RANGE, + NULL); klass->toggle_format = webview_toggle_format; klass->clear_format = webview_clear_formatting; @@ -659,6 +1440,10 @@ priv->load_queue = g_queue_new(); + priv->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gtk_smiley_tree_destroy); + priv->default_smilies = gtk_smiley_tree_new(); + g_signal_connect(G_OBJECT(webview), "button-press-event", G_CALLBACK(webview_button_pressed), NULL); @@ -824,6 +1609,28 @@ } void +gtk_webview_set_autoscroll(GtkWebView *webview, gboolean scroll) +{ + GtkWebViewPriv *priv; + + g_return_if_fail(webview != NULL); + + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + priv->autoscroll = scroll; +} + +gboolean +gtk_webview_get_autoscroll(GtkWebView *webview) +{ + GtkWebViewPriv *priv; + + g_return_val_if_fail(webview != NULL, FALSE); + + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + return priv->autoscroll; +} + +void gtk_webview_page_up(GtkWebView *webview) { GtkWebViewPriv *priv; @@ -1098,16 +1905,20 @@ WebKitDOMDocument *dom; WebKitDOMDOMWindow *win; WebKitDOMDOMSelection *sel; - WebKitDOMRange *range; + WebKitDOMRange *range = NULL; g_return_val_if_fail(webview != NULL, NULL); dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); win = webkit_dom_document_get_default_view(dom); sel = webkit_dom_dom_window_get_selection(win); - range = webkit_dom_dom_selection_get_range_at(sel, 0, NULL); + if (webkit_dom_dom_selection_get_range_count(sel)) + range = webkit_dom_dom_selection_get_range_at(sel, 0, NULL); - return webkit_dom_range_get_text(range); + if (range) + return webkit_dom_range_get_text(range); + else + return NULL; } GtkWebViewButtons
--- a/pidgin/gtkwebview.h Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkwebview.h Tue Aug 14 22:26:33 2012 +0200 @@ -58,8 +58,13 @@ GTK_WEBVIEW_ALL = -1 } GtkWebViewButtons; +typedef enum { + GTK_WEBVIEW_SMILEY_CUSTOM = 1 << 0 +} GtkWebViewSmileyFlags; + typedef struct _GtkWebView GtkWebView; typedef struct _GtkWebViewClass GtkWebViewClass; +typedef struct _GtkWebViewSmiley GtkWebViewSmiley; struct _GtkWebView { @@ -77,6 +82,7 @@ void (*clear_format)(GtkWebView *); void (*update_format)(GtkWebView *); void (*changed)(GtkWebView *); + void (*html_appended)(GtkWebView *, WebKitDOMRange *); }; G_BEGIN_DECLS @@ -162,6 +168,25 @@ void gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth); /** + * Set whether the GtkWebView stays at its end when HTML content is appended. If + * not already at the end before appending, then scrolling will not occur. + * + * @param webview The GtkWebView object + * @param scroll Whether to automatically scroll + */ +void gtk_webview_set_autoscroll(GtkWebView *webview, gboolean scroll); + +/** + * Set whether the GtkWebView stays at its end when HTML content is appended. If + * not already at the end before appending, then scrolling will not occur. + * + * @param webview The GtkWebView object + * + * @return Whether to automatically scroll + */ +gboolean gtk_webview_get_autoscroll(GtkWebView *webview); + +/** * Scrolls a GtkWebView up by one page. * * @param webview The GtkWebView. @@ -475,6 +500,124 @@ */ void gtk_webview_insert_image(GtkWebView *webview, int id); +/** + * Gets the protocol name associated with this GtkWebView. + * + * @param webview The GtkWebView + */ +const char *gtk_webview_get_protocol_name(GtkWebView *webview); + +/** + * Associates a protocol name with a GtkWebView. + * + * @param webview The GtkWebView + * @param protocol_name The protocol name to associate with the GtkWebView + */ +void gtk_webview_set_protocol_name(GtkWebView *webview, const char *protocol_name); + +/** + * Create a new GtkWebViewSmiley. + * + * @param file The image file for the smiley + * @param shortcut The key shortcut for the smiley + * @param hide @c TRUE if the smiley should be hidden in the smiley dialog, + * @c FALSE otherwise + * @param flags The smiley flags + * + * @return The newly created smiley + */ +GtkWebViewSmiley *gtk_webview_smiley_create(const char *file, + const char *shortcut, + gboolean hide, + GtkWebViewSmileyFlags flags); + +/** + * Reload the image data for the smiley. + * + * @param smiley The smiley to reload + */ +void gtk_webview_smiley_reload(GtkWebViewSmiley *smiley); + +/** + * Destroy a GtkWebViewSmiley. + * + * @param smiley The smiley to destroy + */ +void gtk_webview_smiley_destroy(GtkWebViewSmiley *smiley); + +/** + * Returns the text associated with a smiley. + * + * @param smiley The smiley + * + * @return The text + */ +const char *gtk_webview_smiley_get_smile(const GtkWebViewSmiley *smiley); + +/** + * Returns the file associated with a smiley. + * + * @param smiley The smiley + * + * @return The file + */ +const char *gtk_webview_smiley_get_file(const GtkWebViewSmiley *smiley); + +/** + * Returns the invisibility of a smiley. + * + * @param smiley The smiley + * + * @return The hidden status + */ +gboolean gtk_webview_smiley_get_hidden(const GtkWebViewSmiley *smiley); + +/** + * Returns the flags associated with a smiley. + * + * @param smiley The smiley + * + * @return The flags + */ +GtkWebViewSmileyFlags gtk_webview_smiley_get_flags(const GtkWebViewSmiley *smiley); + +/** + * Returns the smiley object associated with the text. + * + * @param webview The GtkWebView + * @param sml The name of the smiley category + * @param text The text associated with the smiley + */ +GtkWebViewSmiley *gtk_webview_smiley_find(GtkWebView *webview, const char *sml, + const char *text); + +/** + * Associates a smiley with a GtkWebView. + * + * @param webview The GtkWebView + * @param sml The name of the smiley category + * @param smiley The GtkWebViewSmiley to associate + */ +void gtk_webview_associate_smiley(GtkWebView *webview, const char *sml, + GtkWebViewSmiley *smiley); + +/** + * Removes all smileys associated with a GtkWebView. + * + * @param webview The GtkWebView. + */ +void gtk_webview_remove_smileys(GtkWebView *webview); + +/** + * Inserts a smiley at the current location or selection in a GtkWebView. + * + * @param webview The GtkWebView + * @param sml The category of the smiley + * @param smiley The text of the smiley to insert + */ +void gtk_webview_insert_smiley(GtkWebView *webview, const char *sml, + const char *smiley); + G_END_DECLS #endif /* _PIDGIN_WEBVIEW_H_ */
--- a/pidgin/gtkwebviewtoolbar.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkwebviewtoolbar.c Tue Aug 14 22:26:33 2012 +0200 @@ -632,7 +632,6 @@ gtk_widget_grab_focus(toolbar->webview); } -#if 0 static void destroy_smiley_dialog(GtkWebViewToolbar *toolbar) { @@ -648,23 +647,21 @@ close_smiley_dialog(GtkWebViewToolbar *toolbar) { GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->smiley), FALSE); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(priv->smiley), FALSE); return FALSE; } - static void insert_smiley_text(GtkWidget *widget, GtkWebViewToolbar *toolbar) { - GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar); char *smiley_text, *escaped_smiley; smiley_text = g_object_get_data(G_OBJECT(widget), "smiley_text"); escaped_smiley = g_markup_escape_text(smiley_text, -1); gtk_webview_insert_smiley(GTK_WEBVIEW(toolbar->webview), - GTK_WEBVIEW(toolbar->webview)->protocol_name, - escaped_smiley); + gtk_webview_get_protocol_name(GTK_WEBVIEW(toolbar->webview)), + escaped_smiley); g_free(escaped_smiley); @@ -675,34 +672,34 @@ struct smiley_button_list { int width, height; GtkWidget *button; - const GtkIMHtmlSmiley *smiley; + const GtkWebViewSmiley *smiley; struct smiley_button_list *next; }; static struct smiley_button_list * sort_smileys(struct smiley_button_list *ls, GtkWebViewToolbar *toolbar, - int *width, const GtkIMHtmlSmiley *smiley) + int *width, const GtkWebViewSmiley *smiley) { - GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar); GtkWidget *image; GtkWidget *button; GtkRequisition size; struct smiley_button_list *cur; struct smiley_button_list *it, *it_last; - const gchar *filename = smiley->file; - gchar *face = smiley->smile; + const gchar *filename = gtk_webview_smiley_get_file(smiley); + const gchar *face = gtk_webview_smiley_get_smile(smiley); PurpleSmiley *psmiley = NULL; gboolean supports_custom = (gtk_webview_get_format_functions(GTK_WEBVIEW(toolbar->webview)) & GTK_WEBVIEW_CUSTOM_SMILEY); cur = g_new0(struct smiley_button_list, 1); it = ls; - it_last = ls; /* list iterators*/ + it_last = ls; /* list iterators */ image = gtk_image_new_from_file(filename); gtk_widget_size_request(image, &size); - if (size.width > 24 && - smiley->flags & GTK_WEBVIEW_SMILEY_CUSTOM) { /* This is a custom smiley, let's scale it */ + if ((size.width > 24) + && (gtk_webview_smiley_get_flags(smiley) & GTK_WEBVIEW_SMILEY_CUSTOM)) { + /* This is a custom smiley, let's scale it */ GdkPixbuf *pixbuf = NULL; GtkImageType type; @@ -734,7 +731,7 @@ button = gtk_button_new(); gtk_container_add(GTK_CONTAINER(button), image); - g_object_set_data(G_OBJECT(button), "smiley_text", face); + g_object_set_data_full(G_OBJECT(button), "smiley_text", g_strdup(face), g_free); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(insert_smiley_text), toolbar); gtk_widget_set_tooltip_text(button, face); @@ -742,11 +739,12 @@ /* these look really weird with borders */ gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - psmiley = purple_smileys_find_by_shortcut(smiley->smile); + psmiley = purple_smileys_find_by_shortcut(face); /* If this is a "non-custom" smiley, check to see if its shortcut is "shadowed" by any custom smiley. This can only happen if the connection is custom smiley-enabled */ - if (supports_custom && psmiley && !(smiley->flags & GTK_WEBVIEW_SMILEY_CUSTOM)) { + if (supports_custom && psmiley + && !(gtk_webview_smiley_get_flags(smiley) & GTK_WEBVIEW_SMILEY_CUSTOM)) { gchar tip[128]; g_snprintf(tip, sizeof(tip), _("This smiley is disabled because a custom smiley exists for this shortcut:\n %s"), @@ -779,12 +777,12 @@ } static gboolean -smiley_is_unique(GSList *list, GtkIMHtmlSmiley *smiley) +smiley_is_unique(GSList *list, GtkWebViewSmiley *smiley) { - GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar); + const char *file = gtk_webview_smiley_get_file(smiley); while (list) { - GtkIMHtmlSmiley *cur = (GtkIMHtmlSmiley *) list->data; - if (!strcmp(cur->file, smiley->file)) + GtkWebViewSmiley *cur = (GtkWebViewSmiley *)list->data; + if (!strcmp(gtk_webview_smiley_get_file(cur), file)) return FALSE; list = list->next; } @@ -818,7 +816,7 @@ line = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0); for (; list; list = list->next) { - if (custom != !!(list->smiley->flags & GTK_WEBVIEW_SMILEY_CUSTOM)) + if (custom != !!(gtk_webview_smiley_get_flags(list->smiley) & GTK_WEBVIEW_SMILEY_CUSTOM)) continue; gtk_box_pack_start(GTK_BOX(line), list->button, FALSE, FALSE, 0); gtk_widget_show(list->button); @@ -832,12 +830,10 @@ } } } -#endif static void insert_smiley_cb(GtkAction *smiley, GtkWebViewToolbar *toolbar) { -#if 0 GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar); GtkWidget *dialog, *vbox; GtkWidget *smiley_table = NULL; @@ -858,13 +854,13 @@ else smileys = pidgin_themes_get_proto_smileys(NULL); - /* Note: prepend smileys to list to avoid O(n^2) overhead when there is - a large number of smileys... need to revers the list after for the dialog - work... */ - while(smileys) { - GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) smileys->data; - if(!smiley->hidden) { - if(smiley_is_unique(unique_smileys, smiley)) { + /* Note: prepend smileys to list to avoid O(n^2) overhead when there is a + large number of smileys... need to reverse the list after for the dialog + to work... */ + while (smileys) { + GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)smileys->data; + if (!gtk_webview_smiley_get_hidden(smiley)) { + if (smiley_is_unique(unique_smileys, smiley)) { unique_smileys = g_slist_prepend(unique_smileys, smiley); } } @@ -877,7 +873,7 @@ for (iterator = custom_smileys ; iterator ; iterator = g_slist_next(iterator)) { - GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) iterator->data; + GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)iterator->data; unique_smileys = g_slist_prepend(unique_smileys, smiley); } } @@ -913,8 +909,8 @@ /* create list of smileys sorted by height */ while (unique_smileys) { - GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) unique_smileys->data; - if (!smiley->hidden) { + GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)unique_smileys->data; + if (!gtk_webview_smiley_get_hidden(smiley)) { ls = sort_smileys(ls, toolbar, &max_line_width, smiley); } unique_smileys = g_slist_delete_link(unique_smileys, unique_smileys); @@ -942,7 +938,6 @@ g_signal_connect(G_OBJECT(dialog), "button-press-event", (GCallback)smiley_dialog_input_cb, toolbar); } - scrolled = pidgin_make_scrollable(smiley_table, GTK_POLICY_NEVER, GTK_POLICY_NEVER, GTK_SHADOW_NONE, -1, -1); gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0); gtk_widget_show(smiley_table); @@ -981,7 +976,6 @@ priv->smiley_dialog = dialog; gtk_widget_grab_focus(toolbar->webview); -#endif } static void @@ -1347,7 +1341,7 @@ {&priv->image, "InsertImage", PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, N_("_Image"), N_("Insert IM Image"), insert_image_cb, FALSE}, {&priv->link, "InsertLink", PIDGIN_STOCK_TOOLBAR_INSERT_LINK, N_("_Link"), N_("Insert Link"), insert_link_cb, TRUE}, {&priv->hr, "InsertHR", NULL, N_("_Horizontal rule"), N_("Insert Horizontal rule"), insert_hr_cb, FALSE}, - {&priv->smiley, "InsertSmiley", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("_Smile!"), N_("Insert Smiley"), insert_smiley_cb, FALSE}, + {&priv->smiley, "InsertSmiley", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("_Smile!"), N_("Insert Smiley"), insert_smiley_cb, TRUE}, {&priv->attention, "SendAttention", PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, N_("_Attention!"), N_("Send Attention"), send_attention_cb, FALSE}, }; @@ -1637,3 +1631,88 @@ PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention != NULL); } +void +gtk_webviewtoolbar_activate(GtkWebViewToolbar *toolbar, + GtkWebViewToolbarAction action) +{ + GtkWebViewToolbarPriv *priv; + GtkAction *act; + + g_return_if_fail(toolbar != NULL); + + priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar); + switch (action) { + case GTK_WEBVIEWTOOLBAR_ACTION_BOLD: + act = priv->bold; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_ITALIC: + act = priv->italic; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_UNDERLINE: + act = priv->underline; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_STRIKE: + act = priv->strike; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_LARGER: + act = priv->larger_size; + break; + +#if 0 + case GTK_WEBVIEWTOOLBAR_ACTION_NORMAL: + act = priv->normal_size; + break; +#endif + + case GTK_WEBVIEWTOOLBAR_ACTION_SMALLER: + act = priv->smaller_size; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_FONTFACE: + act = priv->font; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_FGCOLOR: + act = priv->fgcolor; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_BGCOLOR: + act = priv->bgcolor; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_CLEAR: + act = priv->clear; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_IMAGE: + act = priv->image; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_LINK: + act = priv->link; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_HR: + act = priv->hr; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_SMILEY: + act = priv->smiley; + break; + + case GTK_WEBVIEWTOOLBAR_ACTION_ATTENTION: + act = priv->attention; + break; + + default: + g_return_if_reached(); + break; + } + + gtk_action_activate(act); +} +
--- a/pidgin/gtkwebviewtoolbar.h Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/gtkwebviewtoolbar.h Tue Aug 14 22:26:33 2012 +0200 @@ -35,6 +35,27 @@ #define GTK_IS_WEBVIEWTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_WEBVIEWTOOLBAR)) #define GTK_WEBVIEWTOOLBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_WEBVIEWTOOLBAR, GtkWebViewToolbarClass)) +typedef enum { + GTK_WEBVIEWTOOLBAR_ACTION_BOLD, + GTK_WEBVIEWTOOLBAR_ACTION_ITALIC, + GTK_WEBVIEWTOOLBAR_ACTION_UNDERLINE, + GTK_WEBVIEWTOOLBAR_ACTION_STRIKE, + GTK_WEBVIEWTOOLBAR_ACTION_LARGER, +#if 0 + GTK_WEBVIEWTOOLBAR_ACTION_NORMAL, +#endif + GTK_WEBVIEWTOOLBAR_ACTION_SMALLER, + GTK_WEBVIEWTOOLBAR_ACTION_FONTFACE, + GTK_WEBVIEWTOOLBAR_ACTION_FGCOLOR, + GTK_WEBVIEWTOOLBAR_ACTION_BGCOLOR, + GTK_WEBVIEWTOOLBAR_ACTION_CLEAR, + GTK_WEBVIEWTOOLBAR_ACTION_IMAGE, + GTK_WEBVIEWTOOLBAR_ACTION_LINK, + GTK_WEBVIEWTOOLBAR_ACTION_HR, + GTK_WEBVIEWTOOLBAR_ACTION_SMILEY, + GTK_WEBVIEWTOOLBAR_ACTION_ATTENTION +} GtkWebViewToolbarAction; + typedef struct _GtkWebViewToolbar GtkWebViewToolbar; typedef struct _GtkWebViewToolbarClass GtkWebViewToolbarClass; @@ -90,6 +111,15 @@ void gtk_webviewtoolbar_switch_active_conversation(GtkWebViewToolbar *toolbar, PurpleConversation *conv); +/** + * Activate a GtkWebViewToolbar action + * + * @param toolbar The GtkWebViewToolbar object + * @param action The GtkWebViewToolbarAction + */ +void gtk_webviewtoolbar_activate(GtkWebViewToolbar *toolbar, + GtkWebViewToolbarAction action); + G_END_DECLS #endif /* _PIDGINWEBVIEWTOOLBAR_H_ */
--- a/pidgin/plugins/perl/common/Makefile.PL.in Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/plugins/perl/common/Makefile.PL.in Tue Aug 14 22:26:33 2012 +0200 @@ -10,7 +10,7 @@ 'AUTHOR' => 'Pidgin <http://pidgin.im/>') : ()), 'DEFINE' => '@DEBUG_CFLAGS@', 'dynamic_lib' => { 'OTHERLDFLAGS' => '@LDFLAGS@' }, - 'INC' => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_srcdir@/libpurple -I@top_srcdir@/pidgin @GTK_CFLAGS@', + 'INC' => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_srcdir@/libpurple -I@top_srcdir@/pidgin @GTK_CFLAGS@ @WEBKIT_CFLAGS@', 'OBJECT' => '$(O_FILES)', # link all the C files too 'TYPEMAPS' => ["@top_srcdir@/libpurple/plugins/perl/common/typemap"], # 'OPTIMIZE' => '-g', # For debugging.
--- a/pidgin/plugins/vvconfig.c Wed Aug 08 11:22:52 2012 +0200 +++ b/pidgin/plugins/vvconfig.c Tue Aug 14 22:26:33 2012 +0200 @@ -39,7 +39,7 @@ /* "esdmon", "ESD", ? */ "osssrc", "OSS", "pulsesrc", "PulseAudio", - "libsndiosrc", "sndio", + "sndiosrc", "sndio", /* "audiotestsrc wave=silence", "Silence", */ "audiotestsrc", "Test Sound", NULL @@ -51,7 +51,7 @@ "esdsink", "ESD", "osssink", "OSS", "pulsesink", "PulseAudio", - "libsndiosink", "sndio", + "sndiosink", "sndio", NULL };