No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-mail-display.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-mail-display.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * e-mail-display.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include "e-mail-display.h"
27 #include "e-mail-display-popup-extension.h"
28
29 #include <glib/gi18n.h>
30 #include <gdk/gdk.h>
31
32 #include <em-format/e-mail-part-utils.h>
33 #include <em-format/e-mail-formatter-extension.h>
34 #include <em-format/e-mail-extension-registry.h>
35 #include <em-format/e-mail-part-attachment.h>
36 #include <em-format/e-mail-formatter-print.h>
37
38 #include "e-util/e-marshal.h"
39 #include "e-util/e-util.h"
40 #include "e-util/e-plugin-ui.h"
41 #include "e-util/e-file-request.h"
42 #include "e-util/e-stock-request.h"
43 #include "mail/em-composer-utils.h"
44 #include "mail/em-utils.h"
45 #include "mail/e-mail-request.h"
46 #include "mail/e-http-request.h"
47 #include "widgets/misc/e-attachment-bar.h"
48 #include "widgets/misc/e-attachment-button.h"
49
50 #include <camel/camel.h>
51
52 #define d(x)
53
54 G_DEFINE_TYPE (
55 EMailDisplay,
56 e_mail_display,
57 E_TYPE_WEB_VIEW);
58
59 #define E_MAIL_DISPLAY_GET_PRIVATE(obj) \
60 (G_TYPE_INSTANCE_GET_PRIVATE \
61 ((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayPrivate))
62
63 struct _EMailDisplayPrivate {
64 EMailPartList *part_list;
65 EMailFormatterMode mode;
66 EMailFormatter *formatter;
67
68 gboolean headers_collapsable;
69 gboolean headers_collapsed;
70
71 gint force_image_load: 1;
72
73 GSettings *settings;
74
75 GHashTable *widgets;
76
77 guint scheduled_reload;
78 };
79
80 enum {
81 PROP_0,
82 PROP_MODE,
83 PROP_PART_LIST,
84 PROP_HEADERS_COLLAPSABLE,
85 PROP_HEADERS_COLLAPSED,
86 PROP_FORMATTER
87 };
88
89 static CamelDataCache *emd_global_http_cache = NULL;
90
91 static const gchar *ui =
92 "<ui>"
93 " <popup name='context'>"
94 " <placeholder name='custom-actions-1'>"
95 " <menuitem action='add-to-address-book'/>"
96 " <menuitem action='send-reply'/>"
97 " </placeholder>"
98 " <placeholder name='custom-actions-3'>"
99 " <menu action='search-folder-menu'>"
100 " <menuitem action='search-folder-recipient'/>"
101 " <menuitem action='search-folder-sender'/>"
102 " </menu>"
103 " </placeholder>"
104 " </popup>"
105 "</ui>";
106
107 static const gchar *image_ui =
108 "<ui>"
109 " <popup name='context'>"
110 " <placeholder name='custom-actions-2'>"
111 " <menuitem action='image-save'/>"
112 " </placeholder>"
113 " </popup>"
114 "</ui>";
115
116 static GtkActionEntry mailto_entries[] = {
117
118 { "add-to-address-book",
119 "contact-new",
120 N_("_Add to Address Book..."),
121 NULL,
122 NULL, /* XXX Add a tooltip! */
123 NULL /* Handled by EMailReader */ },
124
125 { "search-folder-recipient",
126 NULL,
127 N_("_To This Address"),
128 NULL,
129 NULL, /* XXX Add a tooltip! */
130 NULL /* Handled by EMailReader */ },
131
132 { "search-folder-sender",
133 NULL,
134 N_("_From This Address"),
135 NULL,
136 NULL, /* XXX Add a tooltip! */
137 NULL /* Handled by EMailReader */ },
138
139 { "send-reply",
140 NULL,
141 N_("Send _Reply To..."),
142 NULL,
143 N_("Send a reply message to this address"),
144 NULL /* Handled by EMailReader */ },
145
146 /*** Menus ***/
147
148 { "search-folder-menu",
149 "folder-saved-search",
150 N_("Create Search _Folder"),
151 NULL,
152 NULL,
153 NULL }
154 };
155
156 static GtkActionEntry image_entries[] = {
157
158 { "image-save",
159 GTK_STOCK_SAVE,
160 N_("Save _Image..."),
161 NULL,
162 N_("Save the image to a file"),
163 NULL /* Handled by EMailReader */ },
164 };
165
166 static void
167 formatter_image_loading_policy_changed_cb (GObject *object,
168 GParamSpec *pspec,
169 gpointer user_data)
170 {
171 EMailDisplay *display = user_data;
172 EMailFormatter *formatter = E_MAIL_FORMATTER (object);
173 EMailImageLoadingPolicy policy;
174
175 policy = e_mail_formatter_get_image_loading_policy (formatter);
176
177 if (policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS)
178 e_mail_display_load_images (display);
179 else
180 e_mail_display_reload (display);
181 }
182
183 static gboolean
184 mail_display_image_exists_in_cache (const gchar *image_uri)
185 {
186 gchar *image_filename, *uri_md5;
187 gboolean exists;
188
189 g_return_val_if_fail (emd_global_http_cache != NULL, FALSE);
190
191 uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1);
192 image_filename = camel_data_cache_get_filename (emd_global_http_cache, "http", uri_md5);
193
194 exists = image_filename && g_file_test (image_filename, G_FILE_TEST_EXISTS);
195
196 g_free (uri_md5);
197 g_free (image_filename);
198
199 return exists;
200 }
201
202 static void
203 mail_display_update_formatter_colors (EMailDisplay *display)
204 {
205 GtkStyle *style;
206 GtkStateType state;
207
208 if (display->priv->formatter == NULL)
209 return;
210
211 style = gtk_widget_get_style (GTK_WIDGET (display));
212 state = gtk_widget_get_state (GTK_WIDGET (display));
213 e_mail_formatter_set_style (display->priv->formatter, style, state);
214 }
215
216 static void
217 mail_display_plugin_widget_disconnect_children (GtkWidget *widget,
218 gpointer mail_display)
219 {
220 g_signal_handlers_disconnect_by_data (widget, mail_display);
221 }
222
223 static void
224 mail_display_plugin_widget_disconnect (gpointer widget_uri,
225 gpointer widget,
226 gpointer mail_display)
227 {
228 if (E_IS_ATTACHMENT_BAR (widget))
229 g_signal_handlers_disconnect_by_data (widget, mail_display);
230 else if (E_IS_ATTACHMENT_BUTTON (widget))
231 g_signal_handlers_disconnect_by_data (widget, mail_display);
232 else if (GTK_IS_CONTAINER (widget))
233 gtk_container_foreach (
234 widget,
235 mail_display_plugin_widget_disconnect_children,
236 mail_display);
237 }
238
239 static gboolean
240 mail_display_process_mailto (EWebView *web_view,
241 const gchar *mailto_uri,
242 gpointer user_data)
243 {
244 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
245 g_return_val_if_fail (mailto_uri != NULL, FALSE);
246
247 if (g_ascii_strncasecmp (mailto_uri, "mailto:", 7) == 0) {
248 EShell *shell;
249 EMailPartList *part_list;
250
251 part_list = E_MAIL_DISPLAY (web_view)->priv->part_list;
252
253 shell = e_shell_get_default ();
254 em_utils_compose_new_message_with_mailto (
255 shell, mailto_uri, part_list->folder);
256
257 return TRUE;
258 }
259
260 return FALSE;
261 }
262
263 static gboolean
264 mail_display_link_clicked (WebKitWebView *web_view,
265 WebKitWebFrame *frame,
266 WebKitNetworkRequest *request,
267 WebKitWebNavigationAction *navigation_action,
268 WebKitWebPolicyDecision *policy_decision,
269 gpointer user_data)
270 {
271 const gchar *uri = webkit_network_request_get_uri (request);
272
273 if (g_str_has_prefix (uri, "file://")) {
274 gchar *filename;
275
276 filename = g_filename_from_uri (uri, NULL, NULL);
277
278 if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
279 webkit_web_policy_decision_ignore (policy_decision);
280 webkit_network_request_set_uri (request, "about:blank");
281 g_free (filename);
282 return TRUE;
283 }
284
285 g_free (filename);
286 }
287
288 if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) {
289 /* do nothing, function handled the "mailto:" uri already */
290 webkit_web_policy_decision_ignore (policy_decision);
291 return TRUE;
292
293 } else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) {
294 /* ignore */
295 webkit_web_policy_decision_ignore (policy_decision);
296 return TRUE;
297
298 } else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) {
299 /* ignore */
300 webkit_web_policy_decision_ignore (policy_decision);
301 return TRUE;
302
303 }
304
305 /* Let WebKit handle it. */
306 return FALSE;
307 }
308
309 static void
310 webkit_request_load_from_file (WebKitNetworkRequest *request,
311 const gchar *path)
312 {
313 gchar *data = NULL;
314 gsize length = 0;
315 gchar *b64, *new_uri;
316 gchar *ct;
317
318 if (!g_file_get_contents (path, &data, &length, NULL))
319 return;
320
321 b64 = g_base64_encode ((guchar *) data, length);
322 ct = g_content_type_guess (path, NULL, 0, NULL);
323
324 new_uri = g_strdup_printf ("data:%s;base64,%s", ct, b64);
325 webkit_network_request_set_uri (request, new_uri);
326
327 g_free (b64);
328 g_free (new_uri);
329 g_free (ct);
330 g_free (data);
331 }
332
333 static void
334 mail_display_resource_requested (WebKitWebView *web_view,
335 WebKitWebFrame *frame,
336 WebKitWebResource *resource,
337 WebKitNetworkRequest *request,
338 WebKitNetworkResponse *response,
339 gpointer user_data)
340 {
341 EMailDisplay *display = E_MAIL_DISPLAY (web_view);
342 EMailPartList *part_list;
343 const gchar *uri = webkit_network_request_get_uri (request);
344
345 part_list = display->priv->part_list;
346 if (part_list == NULL)
347 return;
348
349 /* Redirect cid:part_id to mail://mail_id/cid:part_id */
350 if (g_str_has_prefix (uri, "cid:")) {
351 gchar *new_uri;
352
353 /* Always write raw content of CID object. */
354 new_uri = e_mail_part_build_uri (
355 part_list->folder, part_list->message_uid,
356 "part_id", G_TYPE_STRING, uri,
357 "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_CID, NULL);
358
359 webkit_network_request_set_uri (request, new_uri);
360
361 g_free (new_uri);
362
363 /* WebKit won't allow to load a local file when displaying
364 * "remote" mail:// protocol, so we need to handle this manually. */
365 } else if (g_str_has_prefix (uri, "file:")) {
366 gchar *path;
367
368 path = g_filename_from_uri (uri, NULL, NULL);
369 if (path == NULL)
370 return;
371
372 webkit_request_load_from_file (request, path);
373
374 g_free (path);
375
376 /* Redirect http(s) request to evo-http(s) protocol.
377 * See EMailRequest for further details about this. */
378 } else if (g_str_has_prefix (uri, "http:") || g_str_has_prefix (uri, "https:") ||
379 g_str_has_prefix (uri, "evo-http:") || g_str_has_prefix (uri, "evo-https:")) {
380 gchar *new_uri, *mail_uri, *enc;
381 SoupURI *soup_uri;
382 GHashTable *query;
383 gboolean image_exists;
384 EMailImageLoadingPolicy image_policy;
385
386 /* Check Evolution's cache */
387 image_exists = mail_display_image_exists_in_cache (uri);
388
389 /* If the URI is not cached and we are not allowed to load it
390 * then redirect to invalid URI, so that webkit would display
391 * a native placeholder for it. */
392 image_policy = e_mail_formatter_get_image_loading_policy (
393 display->priv->formatter);
394 if (!image_exists && !display->priv->force_image_load &&
395 (image_policy == E_MAIL_IMAGE_LOADING_POLICY_NEVER)) {
396 webkit_network_request_set_uri (request, "about:blank");
397 return;
398 }
399
400 new_uri = g_strconcat ("evo-", uri, NULL);
401 mail_uri = e_mail_part_build_uri (
402 part_list->folder,
403 part_list->message_uid, NULL, NULL);
404
405 soup_uri = soup_uri_new (new_uri);
406 if (soup_uri->query)
407 query = soup_form_decode (soup_uri->query);
408 else
409 query = g_hash_table_new_full (
410 g_str_hash, g_str_equal,
411 g_free, g_free);
412 enc = soup_uri_encode (mail_uri, NULL);
413 g_hash_table_insert (query, g_strdup ("__evo-mail"), enc);
414
415 if (display->priv->force_image_load) {
416 g_hash_table_insert (
417 query,
418 g_strdup ("__evo-load-images"),
419 g_strdup ("true"));
420 }
421
422 g_free (mail_uri);
423
424 soup_uri_set_query_from_form (soup_uri, query);
425 g_free (new_uri);
426
427 new_uri = soup_uri_to_string (soup_uri, FALSE);
428 webkit_network_request_set_uri (request, new_uri);
429
430 g_free (new_uri);
431 soup_uri_free (soup_uri);
432 g_hash_table_unref (query);
433 }
434 }
435
436 static WebKitDOMElement *
437 find_element_by_id (WebKitDOMDocument *document,
438 const gchar *id)
439 {
440 WebKitDOMNodeList *frames;
441 WebKitDOMElement *element;
442 gulong ii, length;
443
444 if (!WEBKIT_DOM_IS_DOCUMENT (document))
445 return NULL;
446
447 /* Try to look up the element in this DOM document */
448 element = webkit_dom_document_get_element_by_id (document, id);
449 if (element != NULL)
450 return element;
451
452 /* If the element is not here then recursively scan all frames */
453 frames = webkit_dom_document_get_elements_by_tag_name (
454 document, "iframe");
455 length = webkit_dom_node_list_get_length (frames);
456 for (ii = 0; ii < length; ii++) {
457 WebKitDOMHTMLIFrameElement *iframe;
458 WebKitDOMDocument *frame_doc;
459 WebKitDOMElement *element;
460
461 iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT (
462 webkit_dom_node_list_item (frames, ii));
463
464 frame_doc = webkit_dom_html_iframe_element_get_content_document (iframe);
465
466 element = find_element_by_id (frame_doc, id);
467
468 if (element != NULL)
469 return element;
470 }
471
472 return NULL;
473 }
474
475 static void
476 mail_display_plugin_widget_resize (GtkWidget *widget,
477 gpointer dummy,
478 EMailDisplay *display)
479 {
480 WebKitDOMElement *parent_element;
481 gchar *dim;
482 gint height, width;
483 gfloat scale;
484
485 parent_element = g_object_get_data (G_OBJECT (widget), "parent_element");
486
487 if (!WEBKIT_DOM_IS_ELEMENT (parent_element)) {
488 d (
489 printf ("%s: %s does not have (valid) parent element!\n",
490 G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
491 return;
492 }
493
494 scale = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (display));
495 width = gtk_widget_get_allocated_width (widget);
496 gtk_widget_get_preferred_height_for_width (widget, width, &height, NULL);
497
498 /* When zooming WebKit does not change dimensions of the elements, but
499 * only scales them on the canvas. GtkWidget can't be scaled though
500 * so we need to cope with the dimensions changes to keep the widgets
501 * still the correct size. Due to inaccuracy in rounding (float -> int)
502 * it still acts a bit funny, but at least it does not cause widgets in
503 * WebKit to go crazy when zooming. */
504 height = height * (1 / scale);
505
506 /* Int -> Str */
507 dim = g_strdup_printf ("%d", height);
508
509 /* Set height of the containment <object> to match height of the
510 * GtkWidget it contains */
511 webkit_dom_html_object_element_set_height (
512 WEBKIT_DOM_HTML_OBJECT_ELEMENT (parent_element), dim);
513 g_free (dim);
514 }
515
516 static void
517 mail_display_plugin_widget_realize_cb (GtkWidget *widget,
518 gpointer user_data)
519 {
520 WebKitDOMHTMLElement *element;
521
522 if (GTK_IS_BOX (widget)) {
523 GList *children;
524
525 children = gtk_container_get_children (GTK_CONTAINER (widget));
526 if (children && children->data &&
527 E_IS_ATTACHMENT_BAR (children->data)) {
528 widget = children->data;
529 }
530
531 g_list_free (children);
532 }
533
534 /* First check if we are actually supposed to be visible */
535 element = g_object_get_data (G_OBJECT (widget), "parent_element");
536 if (element == NULL || !WEBKIT_DOM_IS_HTML_ELEMENT (element)) {
537 g_warning ("UAAAAA");
538 } else {
539 if (webkit_dom_html_element_get_hidden (element)) {
540 gtk_widget_hide (widget);
541 return;
542 }
543 }
544
545 /* Initial resize of the <object> element when the widget
546 * is displayed for the first time. */
547 mail_display_plugin_widget_resize (widget, NULL, user_data);
548 }
549
550 static void
551 plugin_widget_set_parent_element (GtkWidget *widget,
552 EMailDisplay *display)
553 {
554 const gchar *uri;
555 WebKitDOMDocument *document;
556 WebKitDOMElement *element;
557
558 uri = g_object_get_data (G_OBJECT (widget), "uri");
559 if (uri == NULL || *uri == '\0')
560 return;
561
562 document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
563 element = find_element_by_id (document, uri);
564
565 if (!WEBKIT_DOM_IS_ELEMENT (element)) {
566 g_warning ("Failed to find parent <object> for '%s' - no ID set?", uri);
567 return;
568 }
569
570 /* Assign the WebKitDOMElement to "parent_element" data of the GtkWidget
571 * and the GtkWidget to "widget" data of the DOM Element */
572 g_object_set_data (G_OBJECT (widget), "parent_element", element);
573 g_object_set_data (G_OBJECT (element), "widget", widget);
574
575 g_object_bind_property (
576 element, "hidden",
577 widget, "visible",
578 G_BINDING_SYNC_CREATE |
579 G_BINDING_INVERT_BOOLEAN);
580 }
581
582 static void
583 toggle_widget_visibility (EAttachmentButton *button,
584 EMailDisplay *display,
585 WebKitDOMElement *element)
586 {
587 GtkWidget *widget = NULL;
588 gchar *id;
589
590 id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (element));
591 if (id == NULL || *id == '\0')
592 return;
593
594 if (display->priv->widgets != NULL)
595 widget = g_hash_table_lookup (display->priv->widgets, id);
596
597 g_free (id);
598
599 if (widget == NULL)
600 return;
601
602 /* If the widget encapsulates EAttachmentBar then check, whether
603 * the attachment bar is not empty. We want to display it only
604 * when there's at least one attachment */
605 if (GTK_IS_BOX (widget)) {
606 GList *children;
607
608 children = gtk_container_get_children (GTK_CONTAINER (widget));
609 if (children && children->data && E_IS_ATTACHMENT_BAR (children->data)) {
610 EAttachmentStore *store;
611
612 store = e_attachment_bar_get_store (
613 E_ATTACHMENT_BAR (children->data));
614
615 g_list_free (children);
616
617 /* Don't allow to display such attachment bar,
618 * but always allow to hide it */
619 if (e_attachment_button_get_expanded (button) &&
620 (e_attachment_store_get_num_attachments (store) == 0))
621 return;
622 }
623 }
624
625 webkit_dom_html_element_set_hidden (
626 WEBKIT_DOM_HTML_ELEMENT (element),
627 !e_attachment_button_get_expanded (button));
628
629 if (e_attachment_button_get_expanded (button))
630 gtk_widget_show (widget);
631 else
632 gtk_widget_hide (widget);
633 }
634
635 /**
636 * @button: An #EAttachmentButton
637 * @iframe: An iframe element containing document with an attachment
638 * represented by the @button
639 */
640 static void
641 bind_iframe_content_visibility (WebKitDOMElement *iframe,
642 EMailDisplay *display,
643 EAttachmentButton *button)
644 {
645 WebKitDOMDocument *document;
646 WebKitDOMNodeList *nodes;
647 gulong ii, length;
648
649 if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (iframe))
650 return;
651
652 document = webkit_dom_html_iframe_element_get_content_document (
653 WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
654 if (!WEBKIT_DOM_IS_DOCUMENT (document))
655 return;
656
657 nodes = webkit_dom_document_get_elements_by_tag_name (document, "object");
658 length = webkit_dom_node_list_get_length (nodes);
659
660 d ({
661 gchar *name = webkit_dom_html_iframe_element_get_name (
662 WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
663 printf ("Found %ld objects within iframe %s\n", length, name);
664 g_free (name);
665 });
666
667 /* Iterate through all <object>s and bind visibility of their widget
668 * with expanded-state of related attachment button */
669 for (ii = 0; ii < length; ii++) {
670
671 WebKitDOMNode *node = webkit_dom_node_list_item (nodes, ii);
672
673 /* Initial sync */
674 toggle_widget_visibility (
675 button, display, WEBKIT_DOM_ELEMENT (node));
676 }
677 }
678
679 static void
680 attachment_button_expanded (GObject *object,
681 GParamSpec *pspec,
682 gpointer user_data)
683 {
684 EAttachmentButton *button = E_ATTACHMENT_BUTTON (object);
685 EMailDisplay *display = user_data;
686 WebKitDOMDocument *document;
687 WebKitDOMElement *element;
688 WebKitDOMCSSStyleDeclaration *css;
689 gboolean expanded;
690 gchar *id;
691
692 d (
693 printf ("Attachment button %s has been %s!\n",
694 (gchar *) g_object_get_data (object, "uri"),
695 (e_attachment_button_get_expanded (button) ? "expanded" : "collapsed")));
696
697 expanded =
698 e_attachment_button_get_expanded (button) &&
699 gtk_widget_get_visible (GTK_WIDGET (button));
700
701 document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
702 element = find_element_by_id (
703 document, g_object_get_data (object, "attachment_id"));
704
705 if (!WEBKIT_DOM_IS_ELEMENT (element)) {
706 d (
707 printf ("%s: Content <div> of attachment %s does not exist!!\n",
708 G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
709 return;
710 }
711
712 /* Show or hide the DIV which contains
713 * the attachment (iframe, image...). */
714 css = webkit_dom_element_get_style (element);
715 webkit_dom_css_style_declaration_set_property (
716 css, "display", expanded ? "block" : "none", "", NULL);
717
718 id = g_strconcat (
719 g_object_get_data (object, "attachment_id"),
720 ".iframe", NULL);
721 element = find_element_by_id (document, id);
722 g_free (id);
723
724 if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) {
725 d (
726 printf ("%s: No <iframe> found\n",
727 (gchar *) g_object_get_data (object, "attachment_id")));
728 return;
729 }
730 bind_iframe_content_visibility (element, display, button);
731 }
732
733 static void
734 mail_display_attachment_count_changed (EAttachmentStore *store,
735 GParamSpec *pspec,
736 GtkWidget *box)
737 {
738 WebKitDOMHTMLElement *element;
739 GList *children;
740
741 children = gtk_container_get_children (GTK_CONTAINER (box));
742 g_return_if_fail (children && children->data);
743
744 element = g_object_get_data (children->data, "parent_element");
745 g_list_free (children);
746
747 g_return_if_fail (WEBKIT_DOM_IS_HTML_ELEMENT (element));
748
749 if (e_attachment_store_get_num_attachments (store) == 0) {
750 gtk_widget_hide (box);
751 webkit_dom_html_element_set_hidden (element, TRUE);
752 } else {
753 gtk_widget_show (box);
754 webkit_dom_html_element_set_hidden (element, FALSE);
755 }
756 }
757
758 static GtkWidget *
759 mail_display_plugin_widget_requested (WebKitWebView *web_view,
760 gchar *mime_type,
761 gchar *uri,
762 GHashTable *param,
763 gpointer user_data)
764 {
765 EMailDisplay *display;
766 EMailExtensionRegistry *reg;
767 EMailFormatterExtension *extension;
768 GQueue *extensions;
769 GList *iter;
770 EMailPart *part;
771 GtkWidget *widget;
772 gchar *part_id, *type, *object_uri;
773
774 part_id = g_hash_table_lookup (param, "data");
775 if (part_id == NULL || !g_str_has_prefix (uri, "mail://"))
776 return NULL;
777
778 type = g_hash_table_lookup (param, "type");
779 if (type == NULL)
780 return NULL;
781
782 display = E_MAIL_DISPLAY (web_view);
783
784 widget = g_hash_table_lookup (display->priv->widgets, part_id);
785 if (widget != NULL) {
786 d (printf ("Handeled %s widget request from cache\n", part_id));
787 return widget;
788 }
789
790 /* Find the EMailPart representing the requested widget. */
791 part = e_mail_part_list_find_part (display->priv->part_list, part_id);
792 if (part == NULL)
793 return NULL;
794
795 reg = e_mail_formatter_get_extension_registry (display->priv->formatter);
796 extensions = e_mail_extension_registry_get_for_mime_type (reg, type);
797 if (extensions == NULL)
798 return NULL;
799
800 extension = NULL;
801 for (iter = g_queue_peek_head_link (extensions); iter; iter = iter->next) {
802
803 extension = iter->data;
804 if (extension == NULL)
805 continue;
806
807 if (e_mail_formatter_extension_has_widget (extension))
808 break;
809 }
810
811 if (extension == NULL)
812 return NULL;
813
814 /* Get the widget from formatter */
815 widget = e_mail_formatter_extension_get_widget (
816 extension, display->priv->part_list, part, param);
817 d (
818 printf ("Created widget %s (%p) for part %s\n",
819 G_OBJECT_TYPE_NAME (widget), widget, part_id));
820
821 /* Should not happen! WebKit will display an ugly 'Plug-in not
822 * available' placeholder instead of hiding the <object> element. */
823 if (widget == NULL)
824 return NULL;
825
826 /* Attachment button has URI different then the actual PURI because
827 * that URI identifies the attachment itself */
828 if (E_IS_ATTACHMENT_BUTTON (widget)) {
829 EMailPartAttachment *empa = (EMailPartAttachment *) part;
830 gchar *attachment_part_id;
831
832 if (empa->attachment_view_part_id)
833 attachment_part_id = empa->attachment_view_part_id;
834 else
835 attachment_part_id = part_id;
836
837 object_uri = g_strconcat (
838 attachment_part_id, ".attachment_button", NULL);
839 g_object_set_data_full (
840 G_OBJECT (widget), "attachment_id",
841 g_strdup (attachment_part_id),
842 (GDestroyNotify) g_free);
843 } else {
844 object_uri = g_strdup (part_id);
845 }
846
847 /* Store the uri as data of the widget */
848 g_object_set_data_full (
849 G_OBJECT (widget), "uri",
850 object_uri, (GDestroyNotify) g_free);
851
852 /* Set pointer to the <object> element as GObject data
853 * "parent_element" and set pointer to the widget as GObject
854 * data "widget" to the <object> element. */
855 plugin_widget_set_parent_element (widget, display);
856
857 /* Resizing a GtkWidget requires changing size of parent
858 * <object> HTML element in DOM. */
859 g_signal_connect (
860 widget, "realize",
861 G_CALLBACK (mail_display_plugin_widget_realize_cb), display);
862 g_signal_connect (
863 widget, "size-allocate",
864 G_CALLBACK (mail_display_plugin_widget_resize), display);
865
866 if (E_IS_ATTACHMENT_BAR (widget)) {
867 GtkWidget *box = NULL;
868 EAttachmentStore *store;
869
870 /* Only when packed in box (grid does not work),
871 * EAttachmentBar reports correct height */
872 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
873 gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0);
874
875 /* When EAttachmentBar is expanded/collapsed it does not
876 * emit size-allocate signal despite it changes it's height. */
877 g_signal_connect (
878 widget, "notify::expanded",
879 G_CALLBACK (mail_display_plugin_widget_resize),
880 display);
881 g_signal_connect (
882 widget, "notify::active-view",
883 G_CALLBACK (mail_display_plugin_widget_resize),
884 display);
885
886 /* Always hide an attachment bar without attachments */
887 store = e_attachment_bar_get_store (E_ATTACHMENT_BAR (widget));
888 g_signal_connect (
889 store, "notify::num-attachments",
890 G_CALLBACK (mail_display_attachment_count_changed),
891 box);
892
893 gtk_widget_show (widget);
894 gtk_widget_show (box);
895
896 /* Initial sync */
897 mail_display_attachment_count_changed (store, NULL, box);
898
899 widget = box;
900
901 } else if (E_IS_ATTACHMENT_BUTTON (widget)) {
902
903 /* Bind visibility of DOM element containing related
904 * attachment with 'expanded' property of this
905 * attachment button. */
906 WebKitDOMElement *attachment;
907 WebKitDOMDocument *document;
908 EMailPartAttachment *empa = (EMailPartAttachment *) part;
909 gchar *attachment_part_id;
910
911 if (empa->attachment_view_part_id)
912 attachment_part_id = empa->attachment_view_part_id;
913 else
914 attachment_part_id = part_id;
915
916 /* Find attachment-wrapper div which contains
917 * the content of the attachment (iframe). */
918 document = webkit_web_view_get_dom_document (
919 WEBKIT_WEB_VIEW (display));
920 attachment = find_element_by_id (document, attachment_part_id);
921
922 /* None found? Attachment cannot be expanded */
923 if (attachment == NULL) {
924 e_attachment_button_set_expandable (
925 E_ATTACHMENT_BUTTON (widget), FALSE);
926 } else {
927 const CamelContentDisposition *disposition;
928
929 e_attachment_button_set_expandable (
930 E_ATTACHMENT_BUTTON (widget), TRUE);
931
932 /* Show/hide the attachment when the EAttachmentButton
933 * is expanded/collapsed or shown/hidden. */
934 g_signal_connect (
935 widget, "notify::expanded",
936 G_CALLBACK (attachment_button_expanded),
937 display);
938 g_signal_connect (
939 widget, "notify::visible",
940 G_CALLBACK (attachment_button_expanded),
941 display);
942
943 /* Automatically expand attachments that have inline
944 * disposition or the EMailParts have specific
945 * force_inline flag set. */
946 disposition =
947 camel_mime_part_get_content_disposition (part->part);
948 if (!part->force_collapse &&
949 (part->force_inline ||
950 (g_strcmp0 (empa->snoop_mime_type, "message/rfc822") == 0) ||
951 (disposition && disposition->disposition &&
952 g_ascii_strncasecmp (
953 disposition->disposition, "inline", 6) == 0))) {
954
955 e_attachment_button_set_expanded (
956 E_ATTACHMENT_BUTTON (widget), TRUE);
957 } else {
958 e_attachment_button_set_expanded (
959 E_ATTACHMENT_BUTTON (widget), FALSE);
960 attachment_button_expanded (
961 G_OBJECT (widget), NULL, display);
962 }
963 }
964 }
965
966 g_hash_table_insert (
967 display->priv->widgets,
968 g_strdup (object_uri), g_object_ref (widget));
969
970 return widget;
971 }
972
973 static void
974 toggle_headers_visibility (WebKitDOMElement *button,
975 WebKitDOMEvent *event,
976 WebKitWebView *web_view)
977 {
978 WebKitDOMDocument *document;
979 WebKitDOMElement *short_headers, *full_headers;
980 WebKitDOMCSSStyleDeclaration *css_short, *css_full;
981 gboolean expanded;
982 const gchar *path;
983 gchar *css_value;
984
985 document = webkit_web_view_get_dom_document (web_view);
986
987 short_headers = webkit_dom_document_get_element_by_id (
988 document, "__evo-short-headers");
989 if (short_headers == NULL)
990 return;
991
992 css_short = webkit_dom_element_get_style (short_headers);
993
994 full_headers = webkit_dom_document_get_element_by_id (
995 document, "__evo-full-headers");
996 if (full_headers == NULL)
997 return;
998
999 css_full = webkit_dom_element_get_style (full_headers);
1000 css_value = webkit_dom_css_style_declaration_get_property_value (
1001 css_full, "display");
1002 expanded = (g_strcmp0 (css_value, "block") == 0);
1003 g_free (css_value);
1004
1005 webkit_dom_css_style_declaration_set_property (
1006 css_full, "display",
1007 expanded ? "none" : "block", "", NULL);
1008 webkit_dom_css_style_declaration_set_property (
1009 css_short, "display",
1010 expanded ? "block" : "none", "", NULL);
1011
1012 if (expanded)
1013 path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
1014 else
1015 path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
1016
1017 webkit_dom_html_image_element_set_src (
1018 WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
1019
1020 e_mail_display_set_headers_collapsed (
1021 E_MAIL_DISPLAY (web_view), expanded);
1022
1023 d (printf ("Headers %s!\n", expanded ? "collapsed" : "expanded"));
1024 }
1025
1026 static const gchar * addresses[] = { "to", "cc", "bcc" };
1027
1028 static void
1029 toggle_address_visibility (WebKitDOMElement *button,
1030 WebKitDOMEvent *event,
1031 const gchar *address)
1032 {
1033 WebKitDOMElement *full_addr, *ellipsis;
1034 WebKitDOMCSSStyleDeclaration *css_full, *css_ellipsis;
1035 WebKitDOMDocument *document;
1036 gchar *id;
1037 const gchar *path;
1038 gboolean expanded;
1039
1040 document = webkit_dom_node_get_owner_document (
1041 WEBKIT_DOM_NODE (button));
1042
1043 id = g_strconcat ("__evo-moreaddr-", address, NULL);
1044 full_addr = webkit_dom_document_get_element_by_id (document, id);
1045 g_free (id);
1046
1047 if (full_addr == NULL)
1048 return;
1049
1050 css_full = webkit_dom_element_get_style (full_addr);
1051
1052 id = g_strconcat ("__evo-moreaddr-ellipsis-", address, NULL);
1053 ellipsis = webkit_dom_document_get_element_by_id (document, id);
1054 g_free (id);
1055
1056 if (ellipsis == NULL)
1057 return;
1058
1059 css_ellipsis = webkit_dom_element_get_style (ellipsis);
1060
1061 expanded = (g_strcmp0 (
1062 webkit_dom_css_style_declaration_get_property_value (
1063 css_full, "display"), "inline") == 0);
1064
1065 webkit_dom_css_style_declaration_set_property (
1066 css_full, "display", (expanded ? "none" : "inline"), "", NULL);
1067 webkit_dom_css_style_declaration_set_property (
1068 css_ellipsis, "display", (expanded ? "inline" : "none"), "", NULL);
1069
1070 if (expanded)
1071 path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
1072 else
1073 path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
1074
1075 if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (button)) {
1076 id = g_strconcat ("__evo-moreaddr-img-", address, NULL);
1077 button = webkit_dom_document_get_element_by_id (document, id);
1078 g_free (id);
1079
1080 if (button == NULL)
1081 return;
1082 }
1083
1084 webkit_dom_html_image_element_set_src (
1085 WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
1086
1087 }
1088
1089 static void
1090 setup_DOM_bindings (GObject *object,
1091 GParamSpec *pspec,
1092 gpointer user_data)
1093 {
1094 WebKitWebView *web_view;
1095 WebKitWebFrame *frame;
1096 WebKitLoadStatus load_status;
1097 WebKitDOMDocument *document;
1098 WebKitDOMElement *button;
1099 gint ii = 0;
1100
1101 frame = WEBKIT_WEB_FRAME (object);
1102 load_status = webkit_web_frame_get_load_status (frame);
1103 if (load_status != WEBKIT_LOAD_FINISHED)
1104 return;
1105
1106 web_view = webkit_web_frame_get_web_view (frame);
1107 document = webkit_web_view_get_dom_document (web_view);
1108
1109 button = webkit_dom_document_get_element_by_id (
1110 document, "__evo-collapse-headers-img");
1111 if (button != NULL)
1112 webkit_dom_event_target_add_event_listener (
1113 WEBKIT_DOM_EVENT_TARGET (button), "click",
1114 G_CALLBACK (toggle_headers_visibility),
1115 FALSE, web_view);
1116
1117 for (ii = 0; ii < 3; ii++) {
1118 gchar *id;
1119
1120 id = g_strconcat ("__evo-moreaddr-img-", addresses[ii], NULL);
1121 button = webkit_dom_document_get_element_by_id (document, id);
1122 g_free (id);
1123
1124 if (button == NULL)
1125 continue;
1126
1127 webkit_dom_event_target_add_event_listener (
1128 WEBKIT_DOM_EVENT_TARGET (button), "click",
1129 G_CALLBACK (toggle_address_visibility), FALSE,
1130 (gpointer) addresses[ii]);
1131
1132 id = g_strconcat (
1133 "__evo-moreaddr-ellipsis-", addresses[ii], NULL);
1134 button = webkit_dom_document_get_element_by_id (document, id);
1135 g_free (id);
1136
1137 if (button == NULL)
1138 continue;
1139
1140 webkit_dom_event_target_add_event_listener (
1141 WEBKIT_DOM_EVENT_TARGET (button), "click",
1142 G_CALLBACK (toggle_address_visibility), FALSE,
1143 (gpointer) addresses[ii]);
1144 }
1145 }
1146
1147 static void
1148 mail_parts_bind_dom (GObject *object,
1149 GParamSpec *pspec,
1150 gpointer user_data)
1151 {
1152 WebKitWebFrame *frame;
1153 WebKitLoadStatus load_status;
1154 WebKitWebView *web_view;
1155 WebKitDOMDocument *document;
1156 EMailDisplay *display;
1157 GSList *iter;
1158 const gchar *frame_name;
1159
1160 frame = WEBKIT_WEB_FRAME (object);
1161 load_status = webkit_web_frame_get_load_status (frame);
1162
1163 if (load_status != WEBKIT_LOAD_FINISHED)
1164 return;
1165
1166 web_view = webkit_web_frame_get_web_view (frame);
1167 display = E_MAIL_DISPLAY (web_view);
1168 if (display->priv->part_list == NULL)
1169 return;
1170
1171 frame_name = webkit_web_frame_get_name (frame);
1172 if (frame_name == NULL || *frame_name == '\0')
1173 frame_name = ".message.headers";
1174
1175 for (iter = display->priv->part_list->list; iter; iter = iter->next) {
1176
1177 EMailPart *part = iter->data;
1178
1179 if (part == NULL)
1180 continue;
1181
1182 if (g_strcmp0 (part->id, frame_name) == 0)
1183 break;
1184 }
1185
1186 document = webkit_web_view_get_dom_document (web_view);
1187 while (iter != NULL) {
1188 EMailPart *part = iter->data;
1189
1190 if (part == NULL) {
1191 iter = iter->next;
1192 continue;
1193 }
1194
1195 /* Iterate only the parts rendered in
1196 * the frame and all it's subparts. */
1197 if (!g_str_has_prefix (part->id, frame_name))
1198 break;
1199
1200 if (part->bind_func != NULL) {
1201 WebKitDOMElement *element;
1202
1203 element = find_element_by_id (document, part->id);
1204 if (element != NULL) {
1205 d (printf ("/*bind_func*/ for %s\n", part->id));
1206 part->bind_func (part, element);
1207 }
1208 }
1209
1210 iter = iter->next;
1211 }
1212 }
1213
1214 static void
1215 mail_display_frame_created (WebKitWebView *web_view,
1216 WebKitWebFrame *frame,
1217 gpointer user_data)
1218 {
1219 d (printf ("Frame %s created!\n", webkit_web_frame_get_name (frame)));
1220
1221 /* Call bind_func of all parts written in this frame */
1222 g_signal_connect (
1223 frame, "notify::load-status",
1224 G_CALLBACK (mail_parts_bind_dom), NULL);
1225 }
1226
1227 static void
1228 mail_display_uri_changed (EMailDisplay *display,
1229 GParamSpec *pspec,
1230 gpointer dummy)
1231 {
1232 d (printf ("EMailDisplay URI changed, recreating widgets hashtable\n"));
1233
1234 if (display->priv->widgets != NULL) {
1235 g_hash_table_foreach (
1236 display->priv->widgets,
1237 mail_display_plugin_widget_disconnect, display);
1238 g_hash_table_destroy (display->priv->widgets);
1239 }
1240
1241 display->priv->widgets = g_hash_table_new_full (
1242 (GHashFunc) g_str_hash,
1243 (GEqualFunc) g_str_equal,
1244 (GDestroyNotify) g_free,
1245 (GDestroyNotify) g_object_unref);
1246 }
1247
1248 static void
1249 mail_display_set_property (GObject *object,
1250 guint property_id,
1251 const GValue *value,
1252 GParamSpec *pspec)
1253 {
1254 switch (property_id) {
1255 case PROP_PART_LIST:
1256 e_mail_display_set_parts_list (
1257 E_MAIL_DISPLAY (object),
1258 g_value_get_pointer (value));
1259 return;
1260 case PROP_MODE:
1261 e_mail_display_set_mode (
1262 E_MAIL_DISPLAY (object),
1263 g_value_get_int (value));
1264 return;
1265 case PROP_HEADERS_COLLAPSABLE:
1266 e_mail_display_set_headers_collapsable (
1267 E_MAIL_DISPLAY (object),
1268 g_value_get_boolean (value));
1269 return;
1270 case PROP_HEADERS_COLLAPSED:
1271 e_mail_display_set_headers_collapsed (
1272 E_MAIL_DISPLAY (object),
1273 g_value_get_boolean (value));
1274 return;
1275 }
1276
1277 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1278 }
1279
1280 static void
1281 mail_display_get_property (GObject *object,
1282 guint property_id,
1283 GValue *value,
1284 GParamSpec *pspec)
1285 {
1286 switch (property_id) {
1287 case PROP_FORMATTER:
1288 g_value_set_object (
1289 value, e_mail_display_get_formatter (
1290 E_MAIL_DISPLAY (object)));
1291 return;
1292 case PROP_PART_LIST:
1293 g_value_set_pointer (
1294 value, e_mail_display_get_parts_list (
1295 E_MAIL_DISPLAY (object)));
1296 return;
1297 case PROP_MODE:
1298 g_value_set_int (
1299 value, e_mail_display_get_mode (
1300 E_MAIL_DISPLAY (object)));
1301 return;
1302 case PROP_HEADERS_COLLAPSABLE:
1303 g_value_set_boolean (
1304 value, e_mail_display_get_headers_collapsable (
1305 E_MAIL_DISPLAY (object)));
1306 return;
1307 case PROP_HEADERS_COLLAPSED:
1308 g_value_set_boolean (
1309 value, e_mail_display_get_headers_collapsed (
1310 E_MAIL_DISPLAY (object)));
1311 return;
1312 }
1313
1314 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1315 }
1316
1317 static void
1318 mail_display_dispose (GObject *object)
1319 {
1320 EMailDisplayPrivate *priv;
1321
1322 priv = E_MAIL_DISPLAY_GET_PRIVATE (object);
1323
1324 if (priv->scheduled_reload > 0) {
1325 g_source_remove (priv->scheduled_reload);
1326 priv->scheduled_reload = 0;
1327 }
1328
1329 if (priv->widgets != NULL) {
1330 g_hash_table_foreach (
1331 priv->widgets,
1332 mail_display_plugin_widget_disconnect, object);
1333 g_hash_table_destroy (priv->widgets);
1334 priv->widgets = NULL;
1335 }
1336
1337 if (priv->settings != NULL) {
1338 g_signal_handlers_disconnect_matched (
1339 priv->settings, G_SIGNAL_MATCH_DATA,
1340 0, 0, NULL, NULL, object);
1341 g_object_unref (priv->settings);
1342 priv->settings = NULL;
1343 }
1344
1345 g_clear_object (&priv->part_list);
1346 g_clear_object (&priv->formatter);
1347
1348 /* Chain up to parent's dispose() method. */
1349 G_OBJECT_CLASS (e_mail_display_parent_class)->dispose (object);
1350 }
1351
1352 static void
1353 mail_display_constructed (GObject *object)
1354 {
1355 e_extensible_load_extensions (E_EXTENSIBLE (object));
1356
1357 /* Chain up to parent's constructed() method. */
1358 G_OBJECT_CLASS (e_mail_display_parent_class)->constructed (object);
1359 }
1360
1361 static void
1362 mail_display_realize (GtkWidget *widget)
1363 {
1364 /* Chain up to parent's realize() method. */
1365 GTK_WIDGET_CLASS (e_mail_display_parent_class)->realize (widget);
1366
1367 mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
1368 }
1369
1370 static void
1371 mail_display_style_set (GtkWidget *widget,
1372 GtkStyle *previous_style)
1373 {
1374 EMailDisplay *display = E_MAIL_DISPLAY (widget);
1375
1376 mail_display_update_formatter_colors (display);
1377
1378 /* Chain up to parent's style_set() method. */
1379 GTK_WIDGET_CLASS (e_mail_display_parent_class)->
1380 style_set (widget, previous_style);
1381 }
1382
1383 static gboolean
1384 mail_display_button_press_event (GtkWidget *widget,
1385 GdkEventButton *event)
1386 {
1387 WebKitHitTestResult *hit_test;
1388 WebKitHitTestResultContext context;
1389 gchar *image_src;
1390 gboolean visible;
1391 GtkAction *action;
1392 GList *extensions, *iter;
1393 EWebView *web_view = E_WEB_VIEW (widget);
1394
1395 if (event->button != 3)
1396 goto chainup;
1397
1398 hit_test = webkit_web_view_get_hit_test_result (
1399 WEBKIT_WEB_VIEW (web_view), event);
1400
1401 g_object_get (
1402 G_OBJECT (hit_test),
1403 "context", &context,
1404 "image-uri", &image_src,
1405 NULL);
1406
1407 if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE)) {
1408 visible = image_src && g_str_has_prefix (image_src, "cid:");
1409 if (!visible && image_src)
1410 visible = mail_display_image_exists_in_cache (image_src);
1411
1412 if (image_src != NULL)
1413 g_free (image_src);
1414
1415 action = e_web_view_get_action (web_view, "image-save");
1416 if (action != NULL)
1417 gtk_action_set_visible (action, visible);
1418 }
1419
1420 extensions = e_extensible_list_extensions (
1421 E_EXTENSIBLE (web_view), E_TYPE_EXTENSION);
1422 for (iter = extensions; iter; iter = g_list_next (iter)) {
1423 EExtension *extension = iter->data;
1424
1425 if (!E_IS_MAIL_DISPLAY_POPUP_EXTENSION (extension))
1426 continue;
1427
1428 e_mail_display_popup_extension_update_actions (
1429 E_MAIL_DISPLAY_POPUP_EXTENSION (extension), hit_test);
1430 }
1431 g_list_free (extensions);
1432
1433 g_object_unref (hit_test);
1434
1435 chainup:
1436 /* Chain up to parent's button_press_event() method. */
1437 return GTK_WIDGET_CLASS (e_mail_display_parent_class)->
1438 button_press_event (widget, event);
1439 }
1440
1441 static void
1442 mail_display_set_fonts (EWebView *web_view,
1443 PangoFontDescription **monospace,
1444 PangoFontDescription **variable)
1445 {
1446 EMailDisplay *display = E_MAIL_DISPLAY (web_view);
1447 gboolean use_custom_font;
1448 gchar *monospace_font;
1449 gchar *variable_font;
1450
1451 use_custom_font = g_settings_get_boolean (
1452 display->priv->settings, "use-custom-font");
1453 if (!use_custom_font) {
1454 *monospace = NULL;
1455 *variable = NULL;
1456 return;
1457 }
1458
1459 monospace_font = g_settings_get_string (
1460 display->priv->settings, "monospace-font");
1461 variable_font = g_settings_get_string (
1462 display->priv->settings, "variable-width-font");
1463
1464 *monospace = (monospace_font != NULL) ?
1465 pango_font_description_from_string (monospace_font) : NULL;
1466 *variable = (variable_font != NULL) ?
1467 pango_font_description_from_string (variable_font) : NULL;
1468
1469 g_free (monospace_font);
1470 g_free (variable_font);
1471 }
1472
1473 static void
1474 e_mail_display_class_init (EMailDisplayClass *class)
1475 {
1476 GObjectClass *object_class;
1477 EWebViewClass *web_view_class;
1478 GtkWidgetClass *widget_class;
1479
1480 g_type_class_add_private (class, sizeof (EMailDisplayPrivate));
1481
1482 object_class = G_OBJECT_CLASS (class);
1483 object_class->constructed = mail_display_constructed;
1484 object_class->set_property = mail_display_set_property;
1485 object_class->get_property = mail_display_get_property;
1486 object_class->dispose = mail_display_dispose;
1487
1488 widget_class = GTK_WIDGET_CLASS (class);
1489 widget_class->realize = mail_display_realize;
1490 widget_class->style_set = mail_display_style_set;
1491 widget_class->button_press_event = mail_display_button_press_event;
1492
1493 web_view_class = E_WEB_VIEW_CLASS (class);
1494 web_view_class->set_fonts = mail_display_set_fonts;
1495
1496 g_object_class_install_property (
1497 object_class,
1498 PROP_FORMATTER,
1499 g_param_spec_pointer (
1500 "formatter",
1501 "Mail Formatter",
1502 NULL,
1503 G_PARAM_READABLE |
1504 G_PARAM_STATIC_STRINGS));
1505
1506 g_object_class_install_property (
1507 object_class,
1508 PROP_PART_LIST,
1509 g_param_spec_pointer (
1510 "part-list",
1511 "Part List",
1512 NULL,
1513 G_PARAM_READWRITE |
1514 G_PARAM_STATIC_STRINGS));
1515
1516 g_object_class_install_property (
1517 object_class,
1518 PROP_MODE,
1519 g_param_spec_int (
1520 "mode",
1521 "Display Mode",
1522 NULL,
1523 E_MAIL_FORMATTER_MODE_INVALID,
1524 G_MAXINT,
1525 E_MAIL_FORMATTER_MODE_NORMAL,
1526 G_PARAM_READWRITE |
1527 G_PARAM_STATIC_STRINGS));
1528
1529 g_object_class_install_property (
1530 object_class,
1531 PROP_HEADERS_COLLAPSABLE,
1532 g_param_spec_boolean (
1533 "headers-collapsable",
1534 "Headers Collapsable",
1535 NULL,
1536 FALSE,
1537 G_PARAM_READWRITE |
1538 G_PARAM_STATIC_STRINGS));
1539
1540 g_object_class_install_property (
1541 object_class,
1542 PROP_HEADERS_COLLAPSED,
1543 g_param_spec_boolean (
1544 "headers-collapsed",
1545 "Headers Collapsed",
1546 NULL,
1547 FALSE,
1548 G_PARAM_READWRITE |
1549 G_PARAM_STATIC_STRINGS));
1550 }
1551
1552 static void
1553 e_mail_display_init (EMailDisplay *display)
1554 {
1555 GtkUIManager *ui_manager;
1556 const gchar *user_cache_dir;
1557 WebKitWebSettings *settings;
1558 WebKitWebFrame *main_frame;
1559 GtkActionGroup *actions;
1560
1561 display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
1562
1563 /* Set invalid mode so that MODE property initialization is run
1564 * completely (see e_mail_display_set_mode) */
1565 display->priv->mode = E_MAIL_FORMATTER_MODE_INVALID;
1566 e_mail_display_set_mode (display, E_MAIL_FORMATTER_MODE_NORMAL);
1567 display->priv->force_image_load = FALSE;
1568 display->priv->scheduled_reload = 0;
1569
1570 webkit_web_view_set_full_content_zoom (WEBKIT_WEB_VIEW (display), TRUE);
1571
1572 settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (display));
1573 g_object_set (settings, "enable-frame-flattening", TRUE, NULL);
1574
1575 g_signal_connect (
1576 display, "navigation-policy-decision-requested",
1577 G_CALLBACK (mail_display_link_clicked), NULL);
1578 g_signal_connect (
1579 display, "resource-request-starting",
1580 G_CALLBACK (mail_display_resource_requested), NULL);
1581 g_signal_connect (
1582 display, "process-mailto",
1583 G_CALLBACK (mail_display_process_mailto), NULL);
1584 g_signal_connect (
1585 display, "create-plugin-widget",
1586 G_CALLBACK (mail_display_plugin_widget_requested), NULL);
1587 g_signal_connect (
1588 display, "frame-created",
1589 G_CALLBACK (mail_display_frame_created), NULL);
1590 g_signal_connect (
1591 display, "notify::uri",
1592 G_CALLBACK (mail_display_uri_changed), NULL);
1593
1594 display->priv->settings = g_settings_new ("org.gnome.evolution.mail");
1595 g_signal_connect_swapped (
1596 display->priv->settings , "changed::monospace-font",
1597 G_CALLBACK (e_web_view_update_fonts), display);
1598 g_signal_connect_swapped (
1599 display->priv->settings , "changed::variable-width-font",
1600 G_CALLBACK (e_web_view_update_fonts), display);
1601 g_signal_connect_swapped (
1602 display->priv->settings , "changed::use-custom-font",
1603 G_CALLBACK (e_web_view_update_fonts), display);
1604
1605 e_web_view_update_fonts (E_WEB_VIEW (display));
1606
1607 main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display));
1608 g_signal_connect (
1609 main_frame, "notify::load-status",
1610 G_CALLBACK (setup_DOM_bindings), NULL);
1611 g_signal_connect (
1612 main_frame, "notify::load-status",
1613 G_CALLBACK (mail_parts_bind_dom), NULL);
1614
1615 actions = e_web_view_get_action_group (E_WEB_VIEW (display), "mailto");
1616 gtk_action_group_add_actions (
1617 actions, mailto_entries,
1618 G_N_ELEMENTS (mailto_entries), display);
1619 ui_manager = e_web_view_get_ui_manager (E_WEB_VIEW (display));
1620 gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL);
1621
1622 actions = e_web_view_get_action_group (E_WEB_VIEW (display), "image");
1623 gtk_action_group_add_actions (
1624 actions, image_entries,
1625 G_N_ELEMENTS (image_entries), display);
1626 gtk_ui_manager_add_ui_from_string (ui_manager, image_ui, -1, NULL);
1627
1628 e_web_view_install_request_handler (
1629 E_WEB_VIEW (display), E_TYPE_MAIL_REQUEST);
1630 e_web_view_install_request_handler (
1631 E_WEB_VIEW (display), E_TYPE_HTTP_REQUEST);
1632 e_web_view_install_request_handler (
1633 E_WEB_VIEW (display), E_TYPE_FILE_REQUEST);
1634 e_web_view_install_request_handler (
1635 E_WEB_VIEW (display), E_TYPE_STOCK_REQUEST);
1636
1637 if (emd_global_http_cache == NULL) {
1638 user_cache_dir = e_get_user_cache_dir ();
1639 emd_global_http_cache = camel_data_cache_new (user_cache_dir, NULL);
1640
1641 /* cache expiry - 2 hour access, 1 day max */
1642 camel_data_cache_set_expire_age (
1643 emd_global_http_cache, 24 * 60 * 60);
1644 camel_data_cache_set_expire_access (
1645 emd_global_http_cache, 2 * 60 * 60);
1646 }
1647 }
1648
1649 EMailFormatterMode
1650 e_mail_display_get_mode (EMailDisplay *display)
1651 {
1652 g_return_val_if_fail (
1653 E_IS_MAIL_DISPLAY (display),
1654 E_MAIL_FORMATTER_MODE_NORMAL);
1655
1656 return display->priv->mode;
1657 }
1658
1659 void
1660 e_mail_display_set_mode (EMailDisplay *display,
1661 EMailFormatterMode mode)
1662 {
1663 EMailFormatter *formatter;
1664
1665 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1666
1667 if (display->priv->mode == mode)
1668 return;
1669
1670 display->priv->mode = mode;
1671
1672 if (display->priv->mode == E_MAIL_FORMATTER_MODE_PRINTING)
1673 formatter = e_mail_formatter_print_new ();
1674 else
1675 formatter = e_mail_formatter_new ();
1676
1677 g_clear_object (&display->priv->formatter);
1678 display->priv->formatter = formatter;
1679 mail_display_update_formatter_colors (display);
1680
1681 g_signal_connect (
1682 formatter, "notify::image-loading-policy",
1683 G_CALLBACK (formatter_image_loading_policy_changed_cb),
1684 display);
1685
1686 g_object_connect (
1687 formatter,
1688 "swapped-object-signal::notify::charset",
1689 G_CALLBACK (e_mail_display_reload), display,
1690 "swapped-object-signal::notify::image-loading-policy",
1691 G_CALLBACK (e_mail_display_reload), display,
1692 "swapped-object-signal::notify::mark-citations",
1693 G_CALLBACK (e_mail_display_reload), display,
1694 "swapped-object-signal::notify::only-local-photos",
1695 G_CALLBACK (e_mail_display_reload), display,
1696 "swapped-object-signal::notify::show-sender-photo",
1697 G_CALLBACK (e_mail_display_reload), display,
1698 "swapped-object-signal::notify::show-real-date",
1699 G_CALLBACK (e_mail_display_reload), display,
1700 "swapped-object-signal::notify::animate-images",
1701 G_CALLBACK (e_mail_display_reload), display,
1702 "swapped-object-signal::notify::text-color",
1703 G_CALLBACK (e_mail_display_reload), display,
1704 "swapped-object-signal::notify::body-color",
1705 G_CALLBACK (e_mail_display_reload), display,
1706 "swapped-object-signal::notify::citation-color",
1707 G_CALLBACK (e_mail_display_reload), display,
1708 "swapped-object-signal::notify::content-color",
1709 G_CALLBACK (e_mail_display_reload), display,
1710 "swapped-object-signal::notify::frame-color",
1711 G_CALLBACK (e_mail_display_reload), display,
1712 "swapped-object-signal::notify::header-color",
1713 G_CALLBACK (e_mail_display_reload), display,
1714 "swapped-object-signal::need-redraw",
1715 G_CALLBACK (e_mail_display_reload), display,
1716 NULL);
1717
1718 e_mail_display_reload (display);
1719
1720 g_object_notify (G_OBJECT (display), "mode");
1721 }
1722
1723 EMailFormatter *
1724 e_mail_display_get_formatter (EMailDisplay *display)
1725 {
1726 g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
1727
1728 return display->priv->formatter;
1729 }
1730
1731 EMailPartList *
1732 e_mail_display_get_parts_list (EMailDisplay *display)
1733 {
1734 g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
1735
1736 return display->priv->part_list;
1737 }
1738
1739 void
1740 e_mail_display_set_parts_list (EMailDisplay *display,
1741 EMailPartList *part_list)
1742 {
1743 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1744
1745 if (display->priv->part_list == part_list)
1746 return;
1747
1748 if (part_list != NULL) {
1749 g_return_if_fail (E_IS_MAIL_PART_LIST (part_list));
1750 g_object_ref (part_list);
1751 }
1752
1753 if (display->priv->part_list != NULL)
1754 g_object_unref (display->priv->part_list);
1755
1756 display->priv->part_list = part_list;
1757
1758 g_object_notify (G_OBJECT (display), "part-list");
1759 }
1760
1761 gboolean
1762 e_mail_display_get_headers_collapsable (EMailDisplay *display)
1763 {
1764 g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
1765
1766 return display->priv->headers_collapsable;
1767 }
1768
1769 void
1770 e_mail_display_set_headers_collapsable (EMailDisplay *display,
1771 gboolean collapsable)
1772 {
1773 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1774
1775 if (display->priv->headers_collapsable == collapsable)
1776 return;
1777
1778 display->priv->headers_collapsable = collapsable;
1779 e_mail_display_reload (display);
1780
1781 g_object_notify (G_OBJECT (display), "headers-collapsable");
1782 }
1783
1784 gboolean
1785 e_mail_display_get_headers_collapsed (EMailDisplay *display)
1786 {
1787 g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
1788
1789 if (display->priv->headers_collapsable)
1790 return display->priv->headers_collapsed;
1791
1792 return FALSE;
1793 }
1794
1795 void
1796 e_mail_display_set_headers_collapsed (EMailDisplay *display,
1797 gboolean collapsed)
1798 {
1799 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1800
1801 if (display->priv->headers_collapsed == collapsed)
1802 return;
1803
1804 display->priv->headers_collapsed = collapsed;
1805
1806 g_object_notify (G_OBJECT (display), "headers-collapsed");
1807 }
1808
1809 void
1810 e_mail_display_load (EMailDisplay *display,
1811 const gchar *msg_uri)
1812 {
1813 EMailPartList *part_list;
1814 const gchar *default_charset, *charset;
1815 gchar *uri;
1816
1817 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1818
1819 display->priv->force_image_load = FALSE;
1820
1821 part_list = display->priv->part_list;
1822 if (part_list == NULL) {
1823 e_web_view_clear (E_WEB_VIEW (display));
1824 return;
1825 }
1826
1827 default_charset = e_mail_formatter_get_default_charset (display->priv->formatter);
1828 charset = e_mail_formatter_get_charset (display->priv->formatter);
1829
1830 if (!default_charset)
1831 default_charset = "";
1832 if (!charset)
1833 charset = "";
1834
1835 uri = e_mail_part_build_uri (
1836 part_list->folder, part_list->message_uid,
1837 "mode", G_TYPE_INT, display->priv->mode,
1838 "headers_collapsable", G_TYPE_BOOLEAN, display->priv->headers_collapsable,
1839 "headers_collapsed", G_TYPE_BOOLEAN, display->priv->headers_collapsed,
1840 "formatter_default_charset", G_TYPE_STRING, default_charset,
1841 "formatter_charset", G_TYPE_STRING, charset,
1842 NULL);
1843
1844 e_web_view_load_uri (E_WEB_VIEW (display), uri);
1845
1846 g_free (uri);
1847 }
1848
1849 static gboolean
1850 do_reload_display (EMailDisplay *display)
1851 {
1852 EWebView *web_view;
1853 gchar *uri, *query;
1854 GHashTable *table;
1855 SoupURI *soup_uri;
1856 gchar *mode, *collapsable, *collapsed;
1857 const gchar *default_charset, *charset;
1858
1859 web_view = E_WEB_VIEW (display);
1860 uri = (gchar *) e_web_view_get_uri (web_view);
1861
1862 display->priv->scheduled_reload = 0;
1863
1864 if (uri == NULL || *uri == '\0')
1865 return FALSE;
1866
1867 if (strstr (uri, "?") == NULL) {
1868 e_web_view_reload (web_view);
1869 return FALSE;
1870 }
1871
1872 soup_uri = soup_uri_new (uri);
1873
1874 mode = g_strdup_printf ("%d", display->priv->mode);
1875 collapsable = g_strdup_printf ("%d", display->priv->headers_collapsable);
1876 collapsed = g_strdup_printf ("%d", display->priv->headers_collapsed);
1877 default_charset = e_mail_formatter_get_default_charset (display->priv->formatter);
1878 charset = e_mail_formatter_get_charset (display->priv->formatter);
1879
1880 if (!default_charset)
1881 default_charset = "";
1882 if (!charset)
1883 charset = "";
1884
1885 table = soup_form_decode (soup_uri->query);
1886 g_hash_table_replace (
1887 table, g_strdup ("mode"), mode);
1888 g_hash_table_replace (
1889 table, g_strdup ("headers_collapsable"), collapsable);
1890 g_hash_table_replace (
1891 table, g_strdup ("headers_collapsed"), collapsed);
1892 g_hash_table_replace (
1893 table, g_strdup ("formatter_default_charset"), g_strdup (default_charset));
1894 g_hash_table_replace (
1895 table, g_strdup ("formatter_charset"), g_strdup (charset));
1896
1897 query = soup_form_encode_hash (table);
1898
1899 /* The hash table does not free custom values upon destruction */
1900 g_free (mode);
1901 g_free (collapsable);
1902 g_free (collapsed);
1903 g_hash_table_destroy (table);
1904
1905 soup_uri_set_query (soup_uri, query);
1906 g_free (query);
1907
1908 uri = soup_uri_to_string (soup_uri, FALSE);
1909 e_web_view_load_uri (web_view, uri);
1910 g_free (uri);
1911 soup_uri_free (soup_uri);
1912
1913 return FALSE;
1914 }
1915
1916 void
1917 e_mail_display_reload (EMailDisplay *display)
1918 {
1919 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1920
1921 if (display->priv->scheduled_reload > 0)
1922 return;
1923
1924 /* Schedule reloading if neccessary.
1925 * Prioritize ahead of GTK+ redraws. */
1926 display->priv->scheduled_reload = g_idle_add_full (
1927 G_PRIORITY_HIGH_IDLE,
1928 (GSourceFunc) do_reload_display, display, NULL);
1929 }
1930
1931 GtkAction *
1932 e_mail_display_get_action (EMailDisplay *display,
1933 const gchar *action_name)
1934 {
1935 GtkAction *action;
1936
1937 g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
1938 g_return_val_if_fail (action_name != NULL, NULL);
1939
1940 action = e_web_view_get_action (E_WEB_VIEW (display), action_name);
1941
1942 return action;
1943 }
1944
1945 void
1946 e_mail_display_set_status (EMailDisplay *display,
1947 const gchar *status)
1948 {
1949 gchar *str;
1950
1951 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1952
1953 str = g_strdup_printf (
1954 "<!DOCTYPE HTML>\n<html>\n"
1955 "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\" />\n"
1956 "<title>Evolution Mail Display</title>\n"
1957 "</head>\n"
1958 "<body bgcolor=\"#%06x\" text=\"#%06x\">"
1959 " <style>html, body { height: 100%%; }</style>\n"
1960 " <table border=\"0\" width=\"100%%\" height=\"100%%\">\n"
1961 " <tr height=\"100%%\" valign=\"middle\">\n"
1962 " <td width=\"100%%\" align=\"center\">\n"
1963 " <strong>%s</strong>\n"
1964 " </td>\n"
1965 " </tr>\n"
1966 " </table>\n"
1967 "</body>\n"
1968 "</html>\n",
1969 e_color_to_value ((GdkColor *)
1970 e_mail_formatter_get_color (
1971 display->priv->formatter,
1972 E_MAIL_FORMATTER_COLOR_CONTENT)),
1973 e_color_to_value ((GdkColor *)
1974 e_mail_formatter_get_color (
1975 display->priv->formatter,
1976 E_MAIL_FORMATTER_COLOR_TEXT)),
1977 status);
1978
1979 e_web_view_load_string (E_WEB_VIEW (display), str);
1980
1981 g_free (str);
1982 }
1983
1984 static gchar *
1985 mail_display_get_frame_selection_text (WebKitDOMElement *iframe)
1986 {
1987 WebKitDOMDocument *document;
1988 WebKitDOMDOMWindow *window;
1989 WebKitDOMDOMSelection *selection;
1990 WebKitDOMNodeList *frames;
1991 gulong ii, length;
1992
1993 document = webkit_dom_html_iframe_element_get_content_document (
1994 WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
1995 window = webkit_dom_document_get_default_view (document);
1996 selection = webkit_dom_dom_window_get_selection (window);
1997 if (selection && (webkit_dom_dom_selection_get_range_count (selection) > 0)) {
1998 WebKitDOMRange *range;
1999
2000 range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
2001 if (range != NULL)
2002 return webkit_dom_range_to_string (range, NULL);
2003 }
2004
2005 frames = webkit_dom_document_get_elements_by_tag_name (
2006 document, "IFRAME");
2007 length = webkit_dom_node_list_get_length (frames);
2008 for (ii = 0; ii < length; ii++) {
2009 WebKitDOMNode *node;
2010 gchar *text;
2011
2012 node = webkit_dom_node_list_item (frames, ii);
2013
2014 text = mail_display_get_frame_selection_text (
2015 WEBKIT_DOM_ELEMENT (node));
2016
2017 if (text != NULL)
2018 return text;
2019 }
2020
2021 return NULL;
2022 }
2023
2024 gchar *
2025 e_mail_display_get_selection_plain_text (EMailDisplay *display)
2026 {
2027 WebKitDOMDocument *document;
2028 WebKitDOMNodeList *frames;
2029 gulong ii, length;
2030
2031 g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2032
2033 if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (display)))
2034 return NULL;
2035
2036 document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
2037 frames = webkit_dom_document_get_elements_by_tag_name (document, "IFRAME");
2038 length = webkit_dom_node_list_get_length (frames);
2039
2040 for (ii = 0; ii < length; ii++) {
2041 gchar *text;
2042 WebKitDOMNode *node;
2043
2044 node = webkit_dom_node_list_item (frames, ii);
2045
2046 text = mail_display_get_frame_selection_text (
2047 WEBKIT_DOM_ELEMENT (node));
2048
2049 if (text != NULL)
2050 return text;
2051 }
2052
2053 return NULL;
2054 }
2055
2056 void
2057 e_mail_display_load_images (EMailDisplay *display)
2058 {
2059 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2060
2061 display->priv->force_image_load = TRUE;
2062 e_web_view_reload (E_WEB_VIEW (display));
2063 }
2064
2065 void
2066 e_mail_display_set_force_load_images (EMailDisplay *display,
2067 gboolean force_load_images)
2068 {
2069 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2070
2071 display->priv->force_image_load = force_load_images;
2072 }
2073
2074 void
2075 e_mail_display_set_charset (EMailDisplay *display,
2076 const gchar *charset)
2077 {
2078 g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2079
2080 if (display->priv->formatter != NULL)
2081 e_mail_formatter_set_charset (
2082 display->priv->formatter, charset);
2083 }