Mercurial > pidgin
comparison pidgin/gtkwebview.c @ 32819:2c6510167895 default tip
propagate from branch 'im.pidgin.pidgin.2.x.y' (head 3315c5dfbd0ad16511bdcf865e5b07c02d07df24)
to branch 'im.pidgin.pidgin' (head cbd1eda6bcbf0565ae7766396bb8f6f419cb6a9a)
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Sat, 02 Jun 2012 02:30:49 +0000 |
parents | 5617be6a8413 |
children |
comparison
equal
deleted
inserted
replaced
32818:01ff09d4a463 | 32819:2c6510167895 |
---|---|
1 /* | |
2 * @file gtkwebview.c GTK+ WebKitWebView wrapper class. | |
3 * @ingroup pidgin | |
4 */ | |
5 | |
6 /* pidgin | |
7 * | |
8 * Pidgin is the legal property of its developers, whose names are too numerous | |
9 * to list here. Please refer to the COPYRIGHT file distributed with this | |
10 * source distribution. | |
11 * | |
12 * This program is free software; you can redistribute it and/or modify | |
13 * it under the terms of the GNU General Public License as published by | |
14 * the Free Software Foundation; either version 2 of the License, or | |
15 * (at your option) any later version. | |
16 * | |
17 * This program is distributed in the hope that it will be useful, | |
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 * GNU General Public License for more details. | |
21 * | |
22 * You should have received a copy of the GNU General Public License | |
23 * along with this program; if not, write to the Free Software | |
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA | |
25 * | |
26 */ | |
27 | |
28 #include "internal.h" | |
29 #include "pidgin.h" | |
30 | |
31 #include <gdk/gdkkeysyms.h> | |
32 #include "gtkwebview.h" | |
33 | |
34 #define MAX_FONT_SIZE 7 | |
35 #define MAX_SCROLL_TIME 0.4 /* seconds */ | |
36 #define SCROLL_DELAY 33 /* milliseconds */ | |
37 | |
38 #define GTK_WEBVIEW_GET_PRIVATE(obj) \ | |
39 (G_TYPE_INSTANCE_GET_PRIVATE((obj), GTK_TYPE_WEBVIEW, GtkWebViewPriv)) | |
40 | |
41 enum { | |
42 BUTTONS_UPDATE, | |
43 TOGGLE_FORMAT, | |
44 CLEAR_FORMAT, | |
45 UPDATE_FORMAT, | |
46 LAST_SIGNAL | |
47 }; | |
48 static guint signals[LAST_SIGNAL] = { 0 }; | |
49 | |
50 /****************************************************************************** | |
51 * Structs | |
52 *****************************************************************************/ | |
53 | |
54 typedef struct _GtkWebViewPriv { | |
55 GHashTable *images; /**< a map from id to temporary file for the image */ | |
56 gboolean empty; /**< whether anything has been appended **/ | |
57 | |
58 /* JS execute queue */ | |
59 GQueue *js_queue; | |
60 gboolean is_loading; | |
61 | |
62 /* Scroll adjustments */ | |
63 GtkAdjustment *vadj; | |
64 guint scroll_src; | |
65 GTimer *scroll_time; | |
66 | |
67 /* Format options */ | |
68 GtkWebViewButtons format_functions; | |
69 struct { | |
70 gboolean wbfo:1; /* Whole buffer formatting only. */ | |
71 } edit; | |
72 | |
73 } GtkWebViewPriv; | |
74 | |
75 /****************************************************************************** | |
76 * Globals | |
77 *****************************************************************************/ | |
78 | |
79 static WebKitWebViewClass *parent_class = NULL; | |
80 | |
81 /****************************************************************************** | |
82 * Helpers | |
83 *****************************************************************************/ | |
84 | |
85 static const char * | |
86 get_image_src_from_id(GtkWebViewPriv *priv, int id) | |
87 { | |
88 char *src; | |
89 PurpleStoredImage *img; | |
90 | |
91 if (priv->images) { | |
92 /* Check for already loaded image */ | |
93 src = (char *)g_hash_table_lookup(priv->images, GINT_TO_POINTER(id)); | |
94 if (src) | |
95 return src; | |
96 } else { | |
97 priv->images = g_hash_table_new_full(g_direct_hash, g_direct_equal, | |
98 NULL, g_free); | |
99 } | |
100 | |
101 /* Find image in store */ | |
102 img = purple_imgstore_find_by_id(id); | |
103 | |
104 src = (char *)purple_imgstore_get_filename(img); | |
105 if (src) { | |
106 src = g_strdup_printf("file://%s", src); | |
107 } else { | |
108 char *tmp; | |
109 tmp = purple_base64_encode(purple_imgstore_get_data(img), | |
110 purple_imgstore_get_size(img)); | |
111 src = g_strdup_printf("data:base64,%s", tmp); | |
112 g_free(tmp); | |
113 } | |
114 | |
115 g_hash_table_insert(priv->images, GINT_TO_POINTER(id), src); | |
116 | |
117 return src; | |
118 } | |
119 | |
120 /* | |
121 * Replace all <img id=""> tags with <img src="">. I hoped to never | |
122 * write any HTML parsing code, but I'm forced to do this, until | |
123 * purple changes the way it works. | |
124 */ | |
125 static char * | |
126 replace_img_id_with_src(GtkWebViewPriv *priv, const char *html) | |
127 { | |
128 GString *buffer = g_string_new(NULL); | |
129 const char *cur = html; | |
130 char *id; | |
131 int nid; | |
132 | |
133 while (*cur) { | |
134 const char *img = strstr(cur, "<img"); | |
135 if (!img) { | |
136 g_string_append(buffer, cur); | |
137 break; | |
138 } else | |
139 g_string_append_len(buffer, cur, img - cur); | |
140 | |
141 cur = strstr(img, "/>"); | |
142 if (!cur) | |
143 cur = strstr(img, ">"); | |
144 | |
145 if (!cur) { /* invalid html? */ | |
146 g_string_printf(buffer, "%s", html); | |
147 break; | |
148 } | |
149 | |
150 if (strstr(img, "src=") || !strstr(img, "id=")) { | |
151 g_string_printf(buffer, "%s", html); | |
152 break; | |
153 } | |
154 | |
155 /* | |
156 * if this is valid HTML, then I can be sure that it | |
157 * has an id= and does not have an src=, since | |
158 * '=' cannot appear in parameters. | |
159 */ | |
160 | |
161 id = strstr(img, "id=") + 3; | |
162 | |
163 /* *id can't be \0, since a ">" appears after this */ | |
164 if (isdigit(*id)) | |
165 nid = atoi(id); | |
166 else | |
167 nid = atoi(id + 1); | |
168 | |
169 /* let's dump this, tag and then dump the src information */ | |
170 g_string_append_len(buffer, img, cur - img); | |
171 | |
172 g_string_append_printf(buffer, " src='%s' ", get_image_src_from_id(priv, nid)); | |
173 } | |
174 | |
175 return g_string_free(buffer, FALSE); | |
176 } | |
177 | |
178 static gboolean | |
179 process_js_script_queue(GtkWebView *webview) | |
180 { | |
181 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
182 char *script; | |
183 | |
184 if (priv->is_loading) | |
185 return FALSE; /* we will be called when loaded */ | |
186 if (!priv->js_queue || g_queue_is_empty(priv->js_queue)) | |
187 return FALSE; /* nothing to do! */ | |
188 | |
189 script = g_queue_pop_head(priv->js_queue); | |
190 webkit_web_view_execute_script(WEBKIT_WEB_VIEW(webview), script); | |
191 g_free(script); | |
192 | |
193 return TRUE; /* there may be more for now */ | |
194 } | |
195 | |
196 static void | |
197 webview_load_started(WebKitWebView *webview, WebKitWebFrame *frame, | |
198 gpointer userdata) | |
199 { | |
200 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
201 | |
202 /* is there a better way to test for is_loading? */ | |
203 priv->is_loading = TRUE; | |
204 } | |
205 | |
206 static void | |
207 webview_load_finished(WebKitWebView *webview, WebKitWebFrame *frame, | |
208 gpointer userdata) | |
209 { | |
210 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
211 | |
212 priv->is_loading = FALSE; | |
213 g_idle_add((GSourceFunc)process_js_script_queue, webview); | |
214 } | |
215 | |
216 static gboolean | |
217 webview_link_clicked(WebKitWebView *webview, | |
218 WebKitWebFrame *frame, | |
219 WebKitNetworkRequest *request, | |
220 WebKitWebNavigationAction *navigation_action, | |
221 WebKitWebPolicyDecision *policy_decision, | |
222 gpointer userdata) | |
223 { | |
224 const gchar *uri; | |
225 WebKitWebNavigationReason reason; | |
226 | |
227 uri = webkit_network_request_get_uri(request); | |
228 reason = webkit_web_navigation_action_get_reason(navigation_action); | |
229 | |
230 if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) { | |
231 /* the gtk imhtml way was to create an idle cb, not sure | |
232 * why, so right now just using purple_notify_uri directly */ | |
233 purple_notify_uri(NULL, uri); | |
234 webkit_web_policy_decision_ignore(policy_decision); | |
235 } else if (reason == WEBKIT_WEB_NAVIGATION_REASON_OTHER) | |
236 webkit_web_policy_decision_use(policy_decision); | |
237 else | |
238 webkit_web_policy_decision_ignore(policy_decision); | |
239 | |
240 return TRUE; | |
241 } | |
242 | |
243 /* | |
244 * Smoothly scroll a WebView. | |
245 * | |
246 * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom. | |
247 */ | |
248 static gboolean | |
249 smooth_scroll_cb(gpointer data) | |
250 { | |
251 GtkWebViewPriv *priv = data; | |
252 GtkAdjustment *adj; | |
253 gdouble max_val; | |
254 gdouble scroll_val; | |
255 | |
256 g_return_val_if_fail(priv->scroll_time != NULL, FALSE); | |
257 | |
258 adj = priv->vadj; | |
259 #if GTK_CHECK_VERSION(2,14,0) | |
260 max_val = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj); | |
261 #else | |
262 max_val = adj->upper - adj->page_size; | |
263 #endif | |
264 scroll_val = gtk_adjustment_get_value(adj) + | |
265 ((max_val - gtk_adjustment_get_value(adj)) / 3); | |
266 | |
267 if (g_timer_elapsed(priv->scroll_time, NULL) > MAX_SCROLL_TIME | |
268 || scroll_val >= max_val) { | |
269 /* time's up. jump to the end and kill the timer */ | |
270 gtk_adjustment_set_value(adj, max_val); | |
271 g_timer_destroy(priv->scroll_time); | |
272 priv->scroll_time = NULL; | |
273 g_source_remove(priv->scroll_src); | |
274 priv->scroll_src = 0; | |
275 return FALSE; | |
276 } | |
277 | |
278 /* scroll by 1/3rd the remaining distance */ | |
279 gtk_adjustment_set_value(adj, scroll_val); | |
280 return TRUE; | |
281 } | |
282 | |
283 static gboolean | |
284 scroll_idle_cb(gpointer data) | |
285 { | |
286 GtkWebViewPriv *priv = data; | |
287 GtkAdjustment *adj = priv->vadj; | |
288 gdouble max_val; | |
289 | |
290 if (adj) { | |
291 #if GTK_CHECK_VERSION(2,14,0) | |
292 max_val = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj); | |
293 #else | |
294 max_val = adj->upper - adj->page_size; | |
295 #endif | |
296 gtk_adjustment_set_value(adj, max_val); | |
297 } | |
298 | |
299 priv->scroll_src = 0; | |
300 return FALSE; | |
301 } | |
302 | |
303 static void | |
304 webview_clear_formatting(GtkWebView *webview) | |
305 { | |
306 WebKitDOMDocument *dom; | |
307 | |
308 if (!webkit_web_view_get_editable(WEBKIT_WEB_VIEW(webview))) | |
309 return; | |
310 | |
311 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
312 webkit_dom_document_exec_command(dom, "removeFormat", FALSE, ""); | |
313 } | |
314 | |
315 static void | |
316 webview_toggle_format(GtkWebView *webview, GtkWebViewButtons buttons) | |
317 { | |
318 /* since this function is the handler for the formatting keystrokes, | |
319 we need to check here that the formatting attempted is permitted */ | |
320 buttons &= gtk_webview_get_format_functions(webview); | |
321 | |
322 switch (buttons) { | |
323 case GTK_WEBVIEW_BOLD: | |
324 gtk_webview_toggle_bold(webview); | |
325 break; | |
326 case GTK_WEBVIEW_ITALIC: | |
327 gtk_webview_toggle_italic(webview); | |
328 break; | |
329 case GTK_WEBVIEW_UNDERLINE: | |
330 gtk_webview_toggle_underline(webview); | |
331 break; | |
332 case GTK_WEBVIEW_STRIKE: | |
333 gtk_webview_toggle_strike(webview); | |
334 break; | |
335 case GTK_WEBVIEW_SHRINK: | |
336 gtk_webview_font_shrink(webview); | |
337 break; | |
338 case GTK_WEBVIEW_GROW: | |
339 gtk_webview_font_grow(webview); | |
340 break; | |
341 default: | |
342 break; | |
343 } | |
344 } | |
345 | |
346 /****************************************************************************** | |
347 * GObject Stuff | |
348 *****************************************************************************/ | |
349 | |
350 GtkWidget * | |
351 gtk_webview_new(void) | |
352 { | |
353 return GTK_WIDGET(g_object_new(gtk_webview_get_type(), NULL)); | |
354 } | |
355 | |
356 static void | |
357 gtk_webview_finalize(GObject *webview) | |
358 { | |
359 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
360 gpointer temp; | |
361 | |
362 while ((temp = g_queue_pop_head(priv->js_queue))) | |
363 g_free(temp); | |
364 g_queue_free(priv->js_queue); | |
365 | |
366 if (priv->images) | |
367 g_hash_table_unref(priv->images); | |
368 | |
369 G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview)); | |
370 } | |
371 | |
372 static void | |
373 gtk_webview_class_init(GtkWebViewClass *klass, gpointer userdata) | |
374 { | |
375 GObjectClass *gobject_class; | |
376 GtkBindingSet *binding_set; | |
377 | |
378 parent_class = g_type_class_ref(webkit_web_view_get_type()); | |
379 gobject_class = G_OBJECT_CLASS(klass); | |
380 | |
381 g_type_class_add_private(klass, sizeof(GtkWebViewPriv)); | |
382 | |
383 signals[BUTTONS_UPDATE] = g_signal_new("allowed-formats-updated", | |
384 G_TYPE_FROM_CLASS(gobject_class), | |
385 G_SIGNAL_RUN_FIRST, | |
386 G_STRUCT_OFFSET(GtkWebViewClass, buttons_update), | |
387 NULL, 0, g_cclosure_marshal_VOID__INT, | |
388 G_TYPE_NONE, 1, G_TYPE_INT); | |
389 signals[TOGGLE_FORMAT] = g_signal_new("format-toggled", | |
390 G_TYPE_FROM_CLASS(gobject_class), | |
391 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | |
392 G_STRUCT_OFFSET(GtkWebViewClass, toggle_format), | |
393 NULL, 0, g_cclosure_marshal_VOID__INT, | |
394 G_TYPE_NONE, 1, G_TYPE_INT); | |
395 signals[CLEAR_FORMAT] = g_signal_new("format-cleared", | |
396 G_TYPE_FROM_CLASS(gobject_class), | |
397 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, | |
398 G_STRUCT_OFFSET(GtkWebViewClass, clear_format), | |
399 NULL, 0, g_cclosure_marshal_VOID__VOID, | |
400 G_TYPE_NONE, 0); | |
401 signals[UPDATE_FORMAT] = g_signal_new("format-updated", | |
402 G_TYPE_FROM_CLASS(gobject_class), | |
403 G_SIGNAL_RUN_FIRST, | |
404 G_STRUCT_OFFSET(GtkWebViewClass, update_format), | |
405 NULL, 0, g_cclosure_marshal_VOID__VOID, | |
406 G_TYPE_NONE, 0); | |
407 | |
408 klass->toggle_format = webview_toggle_format; | |
409 klass->clear_format = webview_clear_formatting; | |
410 | |
411 gobject_class->finalize = gtk_webview_finalize; | |
412 | |
413 binding_set = gtk_binding_set_by_class(parent_class); | |
414 gtk_binding_entry_add_signal(binding_set, GDK_KEY_b, GDK_CONTROL_MASK, | |
415 "format-toggled", 1, G_TYPE_INT, | |
416 GTK_WEBVIEW_BOLD); | |
417 gtk_binding_entry_add_signal(binding_set, GDK_KEY_i, GDK_CONTROL_MASK, | |
418 "format-toggled", 1, G_TYPE_INT, | |
419 GTK_WEBVIEW_ITALIC); | |
420 gtk_binding_entry_add_signal(binding_set, GDK_KEY_u, GDK_CONTROL_MASK, | |
421 "format-toggled", 1, G_TYPE_INT, | |
422 GTK_WEBVIEW_UNDERLINE); | |
423 gtk_binding_entry_add_signal(binding_set, GDK_KEY_plus, GDK_CONTROL_MASK, | |
424 "format-toggled", 1, G_TYPE_INT, | |
425 GTK_WEBVIEW_GROW); | |
426 gtk_binding_entry_add_signal(binding_set, GDK_KEY_equal, GDK_CONTROL_MASK, | |
427 "format-toggled", 1, G_TYPE_INT, | |
428 GTK_WEBVIEW_GROW); | |
429 gtk_binding_entry_add_signal(binding_set, GDK_KEY_minus, GDK_CONTROL_MASK, | |
430 "format-toggled", 1, G_TYPE_INT, | |
431 GTK_WEBVIEW_SHRINK); | |
432 | |
433 binding_set = gtk_binding_set_by_class(klass); | |
434 gtk_binding_entry_add_signal(binding_set, GDK_KEY_r, GDK_CONTROL_MASK, | |
435 "format-cleared", 0); | |
436 } | |
437 | |
438 static void | |
439 gtk_webview_init(GtkWebView *webview, gpointer userdata) | |
440 { | |
441 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
442 | |
443 priv->empty = TRUE; | |
444 priv->js_queue = g_queue_new(); | |
445 | |
446 g_signal_connect(webview, "navigation-policy-decision-requested", | |
447 G_CALLBACK(webview_link_clicked), NULL); | |
448 | |
449 g_signal_connect(webview, "load-started", | |
450 G_CALLBACK(webview_load_started), NULL); | |
451 | |
452 g_signal_connect(webview, "load-finished", | |
453 G_CALLBACK(webview_load_finished), NULL); | |
454 } | |
455 | |
456 GType | |
457 gtk_webview_get_type(void) | |
458 { | |
459 static GType mview_type = 0; | |
460 if (G_UNLIKELY(mview_type == 0)) { | |
461 static const GTypeInfo mview_info = { | |
462 sizeof(GtkWebViewClass), | |
463 NULL, | |
464 NULL, | |
465 (GClassInitFunc)gtk_webview_class_init, | |
466 NULL, | |
467 NULL, | |
468 sizeof(GtkWebView), | |
469 0, | |
470 (GInstanceInitFunc)gtk_webview_init, | |
471 NULL | |
472 }; | |
473 mview_type = g_type_register_static(webkit_web_view_get_type(), | |
474 "GtkWebView", &mview_info, 0); | |
475 } | |
476 return mview_type; | |
477 } | |
478 | |
479 /***************************************************************************** | |
480 * Public API functions | |
481 *****************************************************************************/ | |
482 | |
483 gboolean | |
484 gtk_webview_is_empty(GtkWebView *webview) | |
485 { | |
486 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
487 return priv->empty; | |
488 } | |
489 | |
490 char * | |
491 gtk_webview_quote_js_string(const char *text) | |
492 { | |
493 GString *str = g_string_new("\""); | |
494 const char *cur = text; | |
495 | |
496 while (cur && *cur) { | |
497 switch (*cur) { | |
498 case '\\': | |
499 g_string_append(str, "\\\\"); | |
500 break; | |
501 case '\"': | |
502 g_string_append(str, "\\\""); | |
503 break; | |
504 case '\r': | |
505 g_string_append(str, "<br/>"); | |
506 break; | |
507 case '\n': | |
508 break; | |
509 default: | |
510 g_string_append_c(str, *cur); | |
511 } | |
512 cur++; | |
513 } | |
514 | |
515 g_string_append_c(str, '"'); | |
516 | |
517 return g_string_free(str, FALSE); | |
518 } | |
519 | |
520 void | |
521 gtk_webview_safe_execute_script(GtkWebView *webview, const char *script) | |
522 { | |
523 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
524 g_queue_push_tail(priv->js_queue, g_strdup(script)); | |
525 g_idle_add((GSourceFunc)process_js_script_queue, webview); | |
526 } | |
527 | |
528 void | |
529 gtk_webview_load_html_string(GtkWebView *webview, const char *html) | |
530 { | |
531 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
532 char *html_imged; | |
533 | |
534 if (priv->images) { | |
535 g_hash_table_unref(priv->images); | |
536 priv->images = NULL; | |
537 } | |
538 | |
539 html_imged = replace_img_id_with_src(priv, html); | |
540 webkit_web_view_load_string(WEBKIT_WEB_VIEW(webview), html_imged, NULL, | |
541 NULL, "file:///"); | |
542 g_free(html_imged); | |
543 } | |
544 | |
545 /* this is a "hack", my plan is to eventually handle this | |
546 * correctly using a signals and a plugin: the plugin will have | |
547 * the information as to what javascript function to call. It seems | |
548 * wrong to hardcode that here. | |
549 */ | |
550 void | |
551 gtk_webview_append_html(GtkWebView *webview, const char *html) | |
552 { | |
553 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
554 WebKitDOMDocument *doc; | |
555 WebKitDOMHTMLElement *body; | |
556 doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
557 body = webkit_dom_document_get_body(doc); | |
558 webkit_dom_html_element_insert_adjacent_html(body, "beforeend", html, NULL); | |
559 priv->empty = FALSE; | |
560 } | |
561 | |
562 void | |
563 gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj) | |
564 { | |
565 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
566 priv->vadj = vadj; | |
567 } | |
568 | |
569 void | |
570 gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth) | |
571 { | |
572 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
573 if (priv->scroll_time) | |
574 g_timer_destroy(priv->scroll_time); | |
575 if (priv->scroll_src) | |
576 g_source_remove(priv->scroll_src); | |
577 if (smooth) { | |
578 priv->scroll_time = g_timer_new(); | |
579 priv->scroll_src = g_timeout_add_full(G_PRIORITY_LOW, SCROLL_DELAY, smooth_scroll_cb, priv, NULL); | |
580 } else { | |
581 priv->scroll_time = NULL; | |
582 priv->scroll_src = g_idle_add_full(G_PRIORITY_LOW, scroll_idle_cb, priv, NULL); | |
583 } | |
584 } | |
585 | |
586 void | |
587 gtk_webview_page_up(GtkWebView *webview) | |
588 { | |
589 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
590 GtkAdjustment *vadj = priv->vadj; | |
591 gdouble scroll_val; | |
592 | |
593 #if GTK_CHECK_VERSION(2,14,0) | |
594 scroll_val = gtk_adjustment_get_value(vadj) - gtk_adjustment_get_page_size(vadj); | |
595 scroll_val = MAX(scroll_val, gtk_adjustment_get_lower(vadj)); | |
596 #else | |
597 scroll_val = gtk_adjustment_get_value(vadj) - vadj->page_size; | |
598 scroll_val = MAX(scroll_val, vadj->lower); | |
599 #endif | |
600 | |
601 gtk_adjustment_set_value(vadj, scroll_val); | |
602 } | |
603 | |
604 void | |
605 gtk_webview_page_down(GtkWebView *webview) | |
606 { | |
607 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
608 GtkAdjustment *vadj = priv->vadj; | |
609 gdouble scroll_val; | |
610 gdouble page_size; | |
611 | |
612 #if GTK_CHECK_VERSION(2,14,0) | |
613 page_size = gtk_adjustment_get_page_size(vadj); | |
614 scroll_val = gtk_adjustment_get_value(vadj) + page_size; | |
615 scroll_val = MIN(scroll_val, gtk_adjustment_get_upper(vadj) - page_size); | |
616 #else | |
617 page_size = vadj->page_size; | |
618 scroll_val = gtk_adjustment_get_value(vadj) + page_size; | |
619 scroll_val = MIN(scroll_val, vadj->upper - page_size); | |
620 #endif | |
621 | |
622 gtk_adjustment_set_value(vadj, scroll_val); | |
623 } | |
624 | |
625 void | |
626 gtk_webview_set_editable(GtkWebView *webview, gboolean editable) | |
627 { | |
628 webkit_web_view_set_editable(WEBKIT_WEB_VIEW(webview), editable); | |
629 } | |
630 | |
631 void | |
632 gtk_webview_setup_entry(GtkWebView *webview, PurpleConnectionFlags flags) | |
633 { | |
634 GtkWebViewButtons buttons; | |
635 | |
636 if (flags & PURPLE_CONNECTION_HTML) { | |
637 char color[8]; | |
638 GdkColor fg_color, bg_color; | |
639 gboolean bold, italic, underline, strike; | |
640 | |
641 buttons = GTK_WEBVIEW_ALL; | |
642 | |
643 if (flags & PURPLE_CONNECTION_NO_BGCOLOR) | |
644 buttons &= ~GTK_WEBVIEW_BACKCOLOR; | |
645 if (flags & PURPLE_CONNECTION_NO_FONTSIZE) | |
646 { | |
647 buttons &= ~GTK_WEBVIEW_GROW; | |
648 buttons &= ~GTK_WEBVIEW_SHRINK; | |
649 } | |
650 if (flags & PURPLE_CONNECTION_NO_URLDESC) | |
651 buttons &= ~GTK_WEBVIEW_LINKDESC; | |
652 | |
653 gtk_webview_get_current_format(webview, &bold, &italic, &underline, &strike); | |
654 | |
655 gtk_webview_set_format_functions(webview, GTK_WEBVIEW_ALL); | |
656 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != bold) | |
657 gtk_webview_toggle_bold(webview); | |
658 | |
659 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != italic) | |
660 gtk_webview_toggle_italic(webview); | |
661 | |
662 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != underline) | |
663 gtk_webview_toggle_underline(webview); | |
664 | |
665 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike") != strike) | |
666 gtk_webview_toggle_strike(webview); | |
667 | |
668 gtk_webview_toggle_fontface(webview, | |
669 purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face")); | |
670 | |
671 if (!(flags & PURPLE_CONNECTION_NO_FONTSIZE)) | |
672 { | |
673 int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size"); | |
674 | |
675 /* 3 is the default. */ | |
676 if (size != 3) | |
677 gtk_webview_font_set_size(webview, size); | |
678 } | |
679 | |
680 if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0) | |
681 { | |
682 gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), | |
683 &fg_color); | |
684 g_snprintf(color, sizeof(color), | |
685 "#%02x%02x%02x", | |
686 fg_color.red / 256, | |
687 fg_color.green / 256, | |
688 fg_color.blue / 256); | |
689 } else | |
690 strcpy(color, ""); | |
691 | |
692 gtk_webview_toggle_forecolor(webview, color); | |
693 | |
694 if (!(flags & PURPLE_CONNECTION_NO_BGCOLOR) && | |
695 strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0) | |
696 { | |
697 gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), | |
698 &bg_color); | |
699 g_snprintf(color, sizeof(color), | |
700 "#%02x%02x%02x", | |
701 bg_color.red / 256, | |
702 bg_color.green / 256, | |
703 bg_color.blue / 256); | |
704 } else | |
705 strcpy(color, ""); | |
706 | |
707 gtk_webview_toggle_backcolor(webview, color); | |
708 | |
709 if (flags & PURPLE_CONNECTION_FORMATTING_WBFO) | |
710 gtk_webview_set_whole_buffer_formatting_only(webview, TRUE); | |
711 else | |
712 gtk_webview_set_whole_buffer_formatting_only(webview, FALSE); | |
713 } else { | |
714 buttons = GTK_WEBVIEW_SMILEY | GTK_WEBVIEW_IMAGE; | |
715 webview_clear_formatting(webview); | |
716 } | |
717 | |
718 if (flags & PURPLE_CONNECTION_NO_IMAGES) | |
719 buttons &= ~GTK_WEBVIEW_IMAGE; | |
720 | |
721 if (flags & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY) | |
722 buttons |= GTK_WEBVIEW_CUSTOM_SMILEY; | |
723 else | |
724 buttons &= ~GTK_WEBVIEW_CUSTOM_SMILEY; | |
725 | |
726 gtk_webview_set_format_functions(webview, buttons); | |
727 } | |
728 | |
729 void | |
730 gtk_webview_set_whole_buffer_formatting_only(GtkWebView *webview, gboolean wbfo) | |
731 { | |
732 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
733 priv->edit.wbfo = wbfo; | |
734 } | |
735 | |
736 void | |
737 gtk_webview_set_format_functions(GtkWebView *webview, GtkWebViewButtons buttons) | |
738 { | |
739 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
740 GObject *object = g_object_ref(G_OBJECT(webview)); | |
741 priv->format_functions = buttons; | |
742 g_signal_emit(object, signals[BUTTONS_UPDATE], 0, buttons); | |
743 g_object_unref(object); | |
744 } | |
745 | |
746 GtkWebViewButtons | |
747 gtk_webview_get_format_functions(GtkWebView *webview) | |
748 { | |
749 GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); | |
750 return priv->format_functions; | |
751 } | |
752 | |
753 void | |
754 gtk_webview_get_current_format(GtkWebView *webview, gboolean *bold, | |
755 gboolean *italic, gboolean *underline, | |
756 gboolean *strike) | |
757 { | |
758 WebKitDOMDocument *dom; | |
759 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
760 | |
761 if (bold) | |
762 *bold = webkit_dom_document_query_command_state(dom, "bold"); | |
763 if (italic) | |
764 *italic = webkit_dom_document_query_command_state(dom, "italic"); | |
765 if (underline) | |
766 *underline = webkit_dom_document_query_command_state(dom, "underline"); | |
767 if (strike) | |
768 *strike = webkit_dom_document_query_command_state(dom, "strikethrough"); | |
769 } | |
770 | |
771 char * | |
772 gtk_webview_get_current_fontface(GtkWebView *webview) | |
773 { | |
774 WebKitDOMDocument *dom; | |
775 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
776 return webkit_dom_document_query_command_value(dom, "fontName"); | |
777 } | |
778 | |
779 char * | |
780 gtk_webview_get_current_forecolor(GtkWebView *webview) | |
781 { | |
782 WebKitDOMDocument *dom; | |
783 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
784 return webkit_dom_document_query_command_value(dom, "foreColor"); | |
785 } | |
786 | |
787 char * | |
788 gtk_webview_get_current_backcolor(GtkWebView *webview) | |
789 { | |
790 WebKitDOMDocument *dom; | |
791 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
792 return webkit_dom_document_query_command_value(dom, "backColor"); | |
793 } | |
794 | |
795 gint | |
796 gtk_webview_get_current_fontsize(GtkWebView *webview) | |
797 { | |
798 WebKitDOMDocument *dom; | |
799 gchar *text; | |
800 gint size; | |
801 | |
802 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
803 text = webkit_dom_document_query_command_value(dom, "fontSize"); | |
804 size = atoi(text); | |
805 g_free(text); | |
806 | |
807 return size; | |
808 } | |
809 | |
810 gboolean | |
811 gtk_webview_get_editable(GtkWebView *webview) | |
812 { | |
813 return webkit_web_view_get_editable(WEBKIT_WEB_VIEW(webview)); | |
814 } | |
815 | |
816 void | |
817 gtk_webview_clear_formatting(GtkWebView *webview) | |
818 { | |
819 GObject *object; | |
820 | |
821 object = g_object_ref(G_OBJECT(webview)); | |
822 g_signal_emit(object, signals[CLEAR_FORMAT], 0); | |
823 | |
824 gtk_widget_grab_focus(GTK_WIDGET(webview)); | |
825 | |
826 g_object_unref(object); | |
827 } | |
828 | |
829 void | |
830 gtk_webview_toggle_bold(GtkWebView *webview) | |
831 { | |
832 WebKitDOMDocument *dom; | |
833 | |
834 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
835 webkit_dom_document_exec_command(dom, "bold", FALSE, ""); | |
836 } | |
837 | |
838 void | |
839 gtk_webview_toggle_italic(GtkWebView *webview) | |
840 { | |
841 WebKitDOMDocument *dom; | |
842 | |
843 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
844 webkit_dom_document_exec_command(dom, "italic", FALSE, ""); | |
845 } | |
846 | |
847 void | |
848 gtk_webview_toggle_underline(GtkWebView *webview) | |
849 { | |
850 WebKitDOMDocument *dom; | |
851 | |
852 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
853 webkit_dom_document_exec_command(dom, "underline", FALSE, ""); | |
854 } | |
855 | |
856 void | |
857 gtk_webview_toggle_strike(GtkWebView *webview) | |
858 { | |
859 WebKitDOMDocument *dom; | |
860 | |
861 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
862 webkit_dom_document_exec_command(dom, "strikethrough", FALSE, ""); | |
863 } | |
864 | |
865 gboolean | |
866 gtk_webview_toggle_forecolor(GtkWebView *webview, const char *color) | |
867 { | |
868 WebKitDOMDocument *dom; | |
869 | |
870 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
871 webkit_dom_document_exec_command(dom, "foreColor", FALSE, color); | |
872 | |
873 return FALSE; | |
874 } | |
875 | |
876 gboolean | |
877 gtk_webview_toggle_backcolor(GtkWebView *webview, const char *color) | |
878 { | |
879 WebKitDOMDocument *dom; | |
880 | |
881 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
882 webkit_dom_document_exec_command(dom, "backColor", FALSE, color); | |
883 | |
884 return FALSE; | |
885 } | |
886 | |
887 gboolean | |
888 gtk_webview_toggle_fontface(GtkWebView *webview, const char *face) | |
889 { | |
890 WebKitDOMDocument *dom; | |
891 | |
892 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
893 webkit_dom_document_exec_command(dom, "fontName", FALSE, face); | |
894 | |
895 return FALSE; | |
896 } | |
897 | |
898 void | |
899 gtk_webview_font_set_size(GtkWebView *webview, gint size) | |
900 { | |
901 WebKitDOMDocument *dom; | |
902 char *tmp; | |
903 | |
904 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
905 tmp = g_strdup_printf("%d", size); | |
906 webkit_dom_document_exec_command(dom, "fontSize", FALSE, tmp); | |
907 g_free(tmp); | |
908 } | |
909 | |
910 void | |
911 gtk_webview_font_shrink(GtkWebView *webview) | |
912 { | |
913 WebKitDOMDocument *dom; | |
914 gint fontsize; | |
915 char *tmp; | |
916 | |
917 fontsize = gtk_webview_get_current_fontsize(webview); | |
918 fontsize = MAX(fontsize - 1, 1); | |
919 | |
920 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
921 tmp = g_strdup_printf("%d", fontsize); | |
922 webkit_dom_document_exec_command(dom, "fontSize", FALSE, tmp); | |
923 g_free(tmp); | |
924 } | |
925 | |
926 void | |
927 gtk_webview_font_grow(GtkWebView *webview) | |
928 { | |
929 WebKitDOMDocument *dom; | |
930 gint fontsize; | |
931 char *tmp; | |
932 | |
933 fontsize = gtk_webview_get_current_fontsize(webview); | |
934 fontsize = MIN(fontsize + 1, MAX_FONT_SIZE); | |
935 | |
936 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview)); | |
937 tmp = g_strdup_printf("%d", fontsize); | |
938 webkit_dom_document_exec_command(dom, "fontSize", FALSE, tmp); | |
939 g_free(tmp); | |
940 } | |
941 |