diff -r 34d89dcecc9b -r e1b50abc07bb pidgin/gtkwebview.c
--- a/pidgin/gtkwebview.c Wed Jun 13 19:33:03 2012 -0400
+++ b/pidgin/gtkwebview.c Fri Jun 15 03:35:32 2012 -0400
@@ -43,6 +43,7 @@
TOGGLE_FORMAT,
CLEAR_FORMAT,
UPDATE_FORMAT,
+ CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
@@ -52,7 +53,6 @@
*****************************************************************************/
typedef struct _GtkWebViewPriv {
- GHashTable *images; /**< a map from id to temporary file for the image */
gboolean empty; /**< whether anything has been appended **/
/* JS execute queue */
@@ -68,6 +68,7 @@
GtkWebViewButtons format_functions;
struct {
gboolean wbfo:1; /* Whole buffer formatting only. */
+ gboolean block_changed:1;
} edit;
} GtkWebViewPriv;
@@ -82,97 +83,44 @@
* Helpers
*****************************************************************************/
-static const char *
-get_image_src_from_id(GtkWebViewPriv *priv, int id)
-{
- char *src;
- PurpleStoredImage *img;
-
- if (priv->images) {
- /* Check for already loaded image */
- src = (char *)g_hash_table_lookup(priv->images, GINT_TO_POINTER(id));
- if (src)
- return src;
- } else {
- priv->images = g_hash_table_new_full(g_direct_hash, g_direct_equal,
- NULL, g_free);
- }
-
- /* Find image in store */
- img = purple_imgstore_find_by_id(id);
-
- src = (char *)purple_imgstore_get_filename(img);
- if (src) {
- src = g_strdup_printf("file://%s", src);
- } else {
- char *tmp;
- tmp = purple_base64_encode(purple_imgstore_get_data(img),
- purple_imgstore_get_size(img));
- src = g_strdup_printf("data:base64,%s", tmp);
- g_free(tmp);
- }
-
- g_hash_table_insert(priv->images, GINT_TO_POINTER(id), src);
-
- return src;
-}
-
-/*
- * Replace all
tags with
. I hoped to never
- * write any HTML parsing code, but I'm forced to do this, until
- * purple changes the way it works.
- */
-static char *
-replace_img_id_with_src(GtkWebViewPriv *priv, const char *html)
+static void
+webview_resource_loading(WebKitWebView *webview,
+ WebKitWebFrame *frame,
+ WebKitWebResource *resource,
+ WebKitNetworkRequest *request,
+ WebKitNetworkResponse *response,
+ gpointer user_data)
{
- GString *buffer = g_string_new(NULL);
- const char *cur = html;
- char *id;
- int nid;
+ const gchar *uri;
- while (*cur) {
- const char *img = strstr(cur, "
");
- if (!cur)
- cur = strstr(img, ">");
+ uri += sizeof(PURPLE_STORED_IMAGE_PROTOCOL) - 1;
+ id = strtoul(uri, NULL, 10);
- if (!cur) { /* invalid html? */
- g_string_printf(buffer, "%s", html);
- break;
- }
+ img = purple_imgstore_find_by_id(id);
+ if (!img)
+ return;
- if (strstr(img, "src=") || !strstr(img, "id=")) {
- g_string_printf(buffer, "%s", html);
- break;
+ filename = purple_imgstore_get_filename(img);
+ if (filename && g_path_is_absolute(filename)) {
+ char *tmp = g_strdup_printf("file://%s", filename);
+ webkit_network_request_set_uri(request, tmp);
+ g_free(tmp);
+ } else {
+ char *b64 = purple_base64_encode(purple_imgstore_get_data(img),
+ purple_imgstore_get_size(img));
+ const char *type = purple_imgstore_get_extension(img);
+ char *tmp = g_strdup_printf("data:image/%s;base64,%s", type, b64);
+ webkit_network_request_set_uri(request, tmp);
+ g_free(b64);
+ g_free(tmp);
}
-
- /*
- * if this is valid HTML, then I can be sure that it
- * has an id= and does not have an src=, since
- * '=' cannot appear in parameters.
- */
-
- id = strstr(img, "id=") + 3;
-
- /* *id can't be \0, since a ">" appears after this */
- if (isdigit(*id))
- nid = atoi(id);
- else
- nid = atoi(id + 1);
-
- /* let's dump this, tag and then dump the src information */
- g_string_append_len(buffer, img, cur - img);
-
- g_string_append_printf(buffer, " src='%s' ", get_image_src_from_id(priv, nid));
}
-
- return g_string_free(buffer, FALSE);
}
static gboolean
@@ -293,15 +241,83 @@
}
static void
+emit_format_signal(GtkWebView *webview, GtkWebViewButtons buttons)
+{
+ g_object_ref(webview);
+ g_signal_emit(webview, signals[TOGGLE_FORMAT], 0, buttons);
+ g_object_unref(webview);
+}
+
+static void
+do_formatting(GtkWebView *webview, const char *name, const char *value)
+{
+ GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+ WebKitDOMDocument *dom;
+ WebKitDOMDOMWindow *win;
+ WebKitDOMDOMSelection *sel = NULL;
+ WebKitDOMRange *range = NULL;
+
+ dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
+
+ if (priv->edit.wbfo) {
+ win = webkit_dom_document_get_default_view(dom);
+ sel = webkit_dom_dom_window_get_selection(win);
+ if (webkit_dom_dom_selection_get_range_count(sel) > 0)
+ range = webkit_dom_dom_selection_get_range_at(sel, 0, NULL);
+ webkit_web_view_select_all(WEBKIT_WEB_VIEW(webview));
+ }
+
+ priv->edit.block_changed = TRUE;
+ webkit_dom_document_exec_command(dom, name, FALSE, value);
+ priv->edit.block_changed = FALSE;
+
+ if (priv->edit.wbfo) {
+ if (range) {
+ webkit_dom_dom_selection_remove_all_ranges(sel);
+ webkit_dom_dom_selection_add_range(sel, range);
+ } else {
+ webkit_dom_dom_selection_collapse_to_end(sel, NULL);
+ }
+ }
+}
+
+static void
+webview_font_shrink(GtkWebView *webview)
+{
+ gint fontsize;
+ char *tmp;
+
+ fontsize = gtk_webview_get_current_fontsize(webview);
+ fontsize = MAX(fontsize - 1, 1);
+
+ tmp = g_strdup_printf("%d", fontsize);
+ do_formatting(webview, "fontSize", tmp);
+ g_free(tmp);
+}
+
+static void
+webview_font_grow(GtkWebView *webview)
+{
+ gint fontsize;
+ char *tmp;
+
+ fontsize = gtk_webview_get_current_fontsize(webview);
+ fontsize = MIN(fontsize + 1, MAX_FONT_SIZE);
+
+ tmp = g_strdup_printf("%d", fontsize);
+ do_formatting(webview, "fontSize", tmp);
+ g_free(tmp);
+}
+
+static void
webview_clear_formatting(GtkWebView *webview)
{
- WebKitDOMDocument *dom;
-
if (!webkit_web_view_get_editable(WEBKIT_WEB_VIEW(webview)))
return;
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- webkit_dom_document_exec_command(dom, "removeFormat", FALSE, "");
+ do_formatting(webview, "removeFormat", "");
+ do_formatting(webview, "unlink", "");
+ do_formatting(webview, "backColor", "inherit");
}
static void
@@ -313,28 +329,36 @@
switch (buttons) {
case GTK_WEBVIEW_BOLD:
- gtk_webview_toggle_bold(webview);
+ do_formatting(webview, "bold", "");
break;
case GTK_WEBVIEW_ITALIC:
- gtk_webview_toggle_italic(webview);
+ do_formatting(webview, "italic", "");
break;
case GTK_WEBVIEW_UNDERLINE:
- gtk_webview_toggle_underline(webview);
+ do_formatting(webview, "underline", "");
break;
case GTK_WEBVIEW_STRIKE:
- gtk_webview_toggle_strike(webview);
+ do_formatting(webview, "strikethrough", "");
break;
case GTK_WEBVIEW_SHRINK:
- gtk_webview_font_shrink(webview);
+ webview_font_shrink(webview);
break;
case GTK_WEBVIEW_GROW:
- gtk_webview_font_grow(webview);
+ webview_font_grow(webview);
break;
default:
break;
}
}
+static void
+editable_input_cb(GtkWebView *webview, gpointer data)
+{
+ GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+ if (!priv->edit.block_changed && gtk_widget_is_sensitive(GTK_WIDGET(webview)))
+ g_signal_emit(webview, signals[CHANGED], 0);
+}
+
/******************************************************************************
* GObject Stuff
*****************************************************************************/
@@ -355,9 +379,6 @@
g_free(temp);
g_queue_free(priv->js_queue);
- if (priv->images)
- g_hash_table_unref(priv->images);
-
G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview));
}
@@ -396,6 +417,12 @@
G_STRUCT_OFFSET(GtkWebViewClass, update_format),
NULL, 0, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+ signals[CHANGED] = g_signal_new("changed",
+ G_TYPE_FROM_CLASS(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(GtkWebViewClass, changed),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
klass->toggle_format = webview_toggle_format;
klass->clear_format = webview_clear_formatting;
@@ -443,6 +470,9 @@
g_signal_connect(webview, "load-finished",
G_CALLBACK(webview_load_finished), NULL);
+
+ g_signal_connect(G_OBJECT(webview), "resource-request-starting",
+ G_CALLBACK(webview_resource_loading), NULL);
}
GType
@@ -520,25 +550,10 @@
void
gtk_webview_load_html_string(GtkWebView *webview, const char *html)
{
- GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
- char *html_imged;
-
- if (priv->images) {
- g_hash_table_unref(priv->images);
- priv->images = NULL;
- }
-
- html_imged = replace_img_id_with_src(priv, html);
- webkit_web_view_load_string(WEBKIT_WEB_VIEW(webview), html_imged, NULL,
- NULL, "file:///");
- g_free(html_imged);
+ webkit_web_view_load_string(WEBKIT_WEB_VIEW(webview), html, NULL, NULL,
+ "file:///");
}
-/* this is a "hack", my plan is to eventually handle this
- * correctly using a signals and a plugin: the plugin will have
- * the information as to what javascript function to call. It seems
- * wrong to hardcode that here.
- */
void
gtk_webview_append_html(GtkWebView *webview, const char *html)
{
@@ -618,6 +633,15 @@
gtk_webview_set_editable(GtkWebView *webview, gboolean editable)
{
webkit_web_view_set_editable(WEBKIT_WEB_VIEW(webview), editable);
+
+ if (editable) {
+ g_signal_connect(G_OBJECT(webview), "user-changed-contents",
+ G_CALLBACK(editable_input_cb), NULL);
+ } else {
+ g_signal_handlers_disconnect_by_func(G_OBJECT(webview),
+ G_CALLBACK(editable_input_cb),
+ NULL);
+ }
}
void
@@ -626,8 +650,6 @@
GtkWebViewButtons buttons;
if (flags & PURPLE_CONNECTION_HTML) {
- char color[8];
- GdkColor fg_color, bg_color;
gboolean bold, italic, underline, strike;
buttons = GTK_WEBVIEW_ALL;
@@ -669,34 +691,15 @@
gtk_webview_font_set_size(webview, size);
}
- if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0)
- {
- gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"),
- &fg_color);
- g_snprintf(color, sizeof(color),
- "#%02x%02x%02x",
- fg_color.red / 256,
- fg_color.green / 256,
- fg_color.blue / 256);
- } else
- strcpy(color, "");
-
- gtk_webview_toggle_forecolor(webview, color);
+ gtk_webview_toggle_forecolor(webview,
+ purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"));
- if (!(flags & PURPLE_CONNECTION_NO_BGCOLOR) &&
- strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0)
- {
- gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"),
- &bg_color);
- g_snprintf(color, sizeof(color),
- "#%02x%02x%02x",
- bg_color.red / 256,
- bg_color.green / 256,
- bg_color.blue / 256);
- } else
- strcpy(color, "");
-
- gtk_webview_toggle_backcolor(webview, color);
+ if (!(flags & PURPLE_CONNECTION_NO_BGCOLOR)) {
+ gtk_webview_toggle_backcolor(webview,
+ purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"));
+ } else {
+ gtk_webview_toggle_backcolor(webview, "");
+ }
if (flags & PURPLE_CONNECTION_FORMATTING_WBFO)
gtk_webview_set_whole_buffer_formatting_only(webview, TRUE);
@@ -719,6 +722,16 @@
}
void
+pidgin_webview_set_spellcheck(GtkWebView *webview, gboolean enable)
+{
+ WebKitWebSettings *settings;
+
+ settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
+ g_object_set(G_OBJECT(settings), "enable-spell-checking", enable, NULL);
+ webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webview), settings);
+}
+
+void
gtk_webview_set_whole_buffer_formatting_only(GtkWebView *webview, gboolean wbfo)
{
GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
@@ -735,6 +748,64 @@
g_object_unref(object);
}
+gchar *
+gtk_webview_get_head_html(GtkWebView *webview)
+{
+ WebKitDOMDocument *doc;
+ WebKitDOMHTMLHeadElement *head;
+ gchar *html;
+
+ doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
+ head = webkit_dom_document_get_head(doc);
+ html = webkit_dom_html_element_get_inner_html(WEBKIT_DOM_HTML_ELEMENT(head));
+
+ return html;
+}
+
+gchar *
+gtk_webview_get_body_html(GtkWebView *webview)
+{
+ WebKitDOMDocument *doc;
+ WebKitDOMHTMLElement *body;
+ gchar *html;
+
+ doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
+ body = webkit_dom_document_get_body(doc);
+ html = webkit_dom_html_element_get_inner_html(body);
+
+ return html;
+}
+
+gchar *
+gtk_webview_get_body_text(GtkWebView *webview)
+{
+ WebKitDOMDocument *doc;
+ WebKitDOMHTMLElement *body;
+ gchar *text;
+
+ doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
+ body = webkit_dom_document_get_body(doc);
+ text = webkit_dom_html_element_get_inner_text(body);
+
+ return text;
+}
+
+gchar *
+gtk_webview_get_selected_text(GtkWebView *webview)
+{
+ WebKitDOMDocument *dom;
+ WebKitDOMDOMWindow *win;
+ WebKitDOMDOMSelection *sel;
+ WebKitDOMRange *range;
+
+ 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);
+
+ return webkit_dom_range_get_text(range);
+}
+
GtkWebViewButtons
gtk_webview_get_format_functions(GtkWebView *webview)
{
@@ -812,55 +883,38 @@
object = g_object_ref(G_OBJECT(webview));
g_signal_emit(object, signals[CLEAR_FORMAT], 0);
-
- gtk_widget_grab_focus(GTK_WIDGET(webview));
-
g_object_unref(object);
}
void
gtk_webview_toggle_bold(GtkWebView *webview)
{
- WebKitDOMDocument *dom;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- webkit_dom_document_exec_command(dom, "bold", FALSE, "");
+ emit_format_signal(webview, GTK_WEBVIEW_BOLD);
}
void
gtk_webview_toggle_italic(GtkWebView *webview)
{
- WebKitDOMDocument *dom;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- webkit_dom_document_exec_command(dom, "italic", FALSE, "");
+ emit_format_signal(webview, GTK_WEBVIEW_ITALIC);
}
void
gtk_webview_toggle_underline(GtkWebView *webview)
{
- WebKitDOMDocument *dom;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- webkit_dom_document_exec_command(dom, "underline", FALSE, "");
+ emit_format_signal(webview, GTK_WEBVIEW_UNDERLINE);
}
void
gtk_webview_toggle_strike(GtkWebView *webview)
{
- WebKitDOMDocument *dom;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- webkit_dom_document_exec_command(dom, "strikethrough", FALSE, "");
+ emit_format_signal(webview, GTK_WEBVIEW_STRIKE);
}
gboolean
gtk_webview_toggle_forecolor(GtkWebView *webview, const char *color)
{
- WebKitDOMDocument *dom;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- webkit_dom_document_exec_command(dom, "foreColor", FALSE, color);
+ do_formatting(webview, "foreColor", color);
+ emit_format_signal(webview, GTK_WEBVIEW_FORECOLOR);
return FALSE;
}
@@ -868,10 +922,8 @@
gboolean
gtk_webview_toggle_backcolor(GtkWebView *webview, const char *color)
{
- WebKitDOMDocument *dom;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- webkit_dom_document_exec_command(dom, "backColor", FALSE, color);
+ do_formatting(webview, "backColor", color);
+ emit_format_signal(webview, GTK_WEBVIEW_BACKCOLOR);
return FALSE;
}
@@ -879,10 +931,8 @@
gboolean
gtk_webview_toggle_fontface(GtkWebView *webview, const char *face)
{
- WebKitDOMDocument *dom;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- webkit_dom_document_exec_command(dom, "fontName", FALSE, face);
+ do_formatting(webview, "fontName", face);
+ emit_format_signal(webview, GTK_WEBVIEW_FACE);
return FALSE;
}
@@ -890,44 +940,67 @@
void
gtk_webview_font_set_size(GtkWebView *webview, gint size)
{
- WebKitDOMDocument *dom;
- char *tmp;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- tmp = g_strdup_printf("%d", size);
- webkit_dom_document_exec_command(dom, "fontSize", FALSE, tmp);
+ char *tmp = g_strdup_printf("%d", size);
+ do_formatting(webview, "fontSize", tmp);
+ emit_format_signal(webview, GTK_WEBVIEW_SHRINK|GTK_WEBVIEW_GROW);
g_free(tmp);
}
void
gtk_webview_font_shrink(GtkWebView *webview)
{
- WebKitDOMDocument *dom;
- gint fontsize;
- char *tmp;
-
- fontsize = gtk_webview_get_current_fontsize(webview);
- fontsize = MAX(fontsize - 1, 1);
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- tmp = g_strdup_printf("%d", fontsize);
- webkit_dom_document_exec_command(dom, "fontSize", FALSE, tmp);
- g_free(tmp);
+ emit_format_signal(webview, GTK_WEBVIEW_SHRINK);
}
void
gtk_webview_font_grow(GtkWebView *webview)
{
+ emit_format_signal(webview, GTK_WEBVIEW_GROW);
+}
+
+void
+gtk_webview_insert_hr(GtkWebView *webview)
+{
+ GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
WebKitDOMDocument *dom;
- gint fontsize;
- char *tmp;
+
+ dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- fontsize = gtk_webview_get_current_fontsize(webview);
- fontsize = MIN(fontsize + 1, MAX_FONT_SIZE);
+ priv->edit.block_changed = TRUE;
+ webkit_dom_document_exec_command(dom, "insertHorizontalRule", FALSE, "");
+ priv->edit.block_changed = FALSE;
+}
+
+void
+gtk_webview_insert_link(GtkWebView *webview, const char *url, const char *desc)
+{
+ GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+ WebKitDOMDocument *dom;
+ char *link;
dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
- tmp = g_strdup_printf("%d", fontsize);
- webkit_dom_document_exec_command(dom, "fontSize", FALSE, tmp);
- g_free(tmp);
+ link = g_strdup_printf("%s", url, desc ? desc : url);
+
+ priv->edit.block_changed = TRUE;
+ webkit_dom_document_exec_command(dom, "insertHTML", FALSE, link);
+ priv->edit.block_changed = FALSE;
+ g_free(link);
}
+void
+gtk_webview_insert_image(GtkWebView *webview, int id)
+{
+ GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+ WebKitDOMDocument *dom;
+ char *img;
+
+ dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
+ img = g_strdup_printf("
",
+ id);
+
+ priv->edit.block_changed = TRUE;
+ webkit_dom_document_exec_command(dom, "insertHTML", FALSE, img);
+ priv->edit.block_changed = FALSE;
+ g_free(img);
+}
+