/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see
*
* Copyright (C) 2011 Dan Vratil
*
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include "e-util/e-util.h"
#include "em-format/e-mail-formatter-print.h"
#include "em-format/e-mail-part-utils.h"
#include "e-mail-printer.h"
#include "e-mail-display.h"
#define w(x)
#define E_MAIL_PRINTER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_MAIL_PRINTER, EMailPrinterPrivate))
enum {
BUTTON_SELECT_ALL,
BUTTON_SELECT_NONE,
BUTTON_TOP,
BUTTON_UP,
BUTTON_DOWN,
BUTTON_BOTTOM,
BUTTONS_COUNT
};
typedef struct _AsyncContext AsyncContext;
struct _EMailPrinterPrivate {
EMailFormatter *formatter;
EMailPartList *part_list;
gchar *export_filename;
GtkListStore *headers;
WebKitWebView *webview; /* WebView to print from */
gchar *uri;
GtkWidget *buttons[BUTTONS_COUNT];
GtkWidget *treeview;
GtkPrintOperation *operation;
GtkPrintOperationAction print_action;
};
struct _AsyncContext {
WebKitWebView *web_view;
gulong load_status_handler_id;
GCancellable *cancellable;
GMainContext *main_context;
GtkPrintOperationAction print_action;
GtkPrintOperationResult print_result;
};
enum {
PROP_0,
PROP_PART_LIST
};
enum {
COLUMN_ACTIVE,
COLUMN_HEADER_NAME,
COLUMN_HEADER_VALUE,
COLUMN_HEADER_STRUCT,
LAST_COLUMN
};
G_DEFINE_TYPE (
EMailPrinter,
e_mail_printer,
G_TYPE_OBJECT);
static void
async_context_free (AsyncContext *async_context)
{
if (async_context->load_status_handler_id > 0)
g_signal_handler_disconnect (
async_context->web_view,
async_context->load_status_handler_id);
g_clear_object (&async_context->web_view);
g_clear_object (&async_context->cancellable);
g_main_context_unref (async_context->main_context);
g_slice_free (AsyncContext, async_context);
}
static gint
mail_printer_header_name_equal (const EMailFormatterHeader *h1,
const EMailFormatterHeader *h2)
{
if ((h2->value == NULL) || (h1->value == NULL)) {
return g_strcmp0 (h1->name, h2->name);
} else {
if ((g_strcmp0 (h1->name, h2->name) == 0) &&
(g_strcmp0 (h1->value, h2->value) == 0))
return 0;
else
return 1;
}
}
static void
mail_printer_draw_footer_cb (GtkPrintOperation *operation,
GtkPrintContext *context,
gint page_nr)
{
PangoFontDescription *desc;
PangoLayout *layout;
gint n_pages;
gdouble width, height;
gchar *text;
cairo_t *cr;
cr = gtk_print_context_get_cairo_context (context);
width = gtk_print_context_get_width (context);
height = gtk_print_context_get_height (context);
g_object_get (operation, "n-pages", &n_pages, NULL);
text = g_strdup_printf (_("Page %d of %d"), page_nr + 1, n_pages);
cairo_set_source_rgb (cr, 0.1, 0.1, 0.1);
cairo_fill (cr);
desc = pango_font_description_from_string ("Sans Regular 10");
layout = gtk_print_context_create_pango_layout (context);
pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
pango_layout_set_font_description (layout, desc);
pango_layout_set_text (layout, text, -1);
pango_layout_set_width (layout, width * PANGO_SCALE);
pango_font_description_free (desc);
cairo_move_to (cr, 0, height + 5);
pango_cairo_show_layout (cr, layout);
g_object_unref (layout);
g_free (text);
}
static void
set_header_visible (EMailPrinter *emp,
EMailFormatterHeader *header,
gint index,
gboolean visible)
{
WebKitDOMDocument *document;
WebKitDOMNodeList *headers;
WebKitDOMElement *element;
WebKitDOMCSSStyleDeclaration *style;
document = webkit_web_view_get_dom_document (emp->priv->webview);
headers = webkit_dom_document_get_elements_by_class_name (document, "header-item");
g_return_if_fail (index < webkit_dom_node_list_get_length (headers));
element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (headers, index));
style = webkit_dom_element_get_style (element);
webkit_dom_css_style_declaration_set_property (
style,
"display", (visible ? "table-row" : "none"), "", NULL);
}
static void
header_active_renderer_toggled_cb (GtkCellRendererToggle *renderer,
gchar *path,
EMailPrinter *emp)
{
GtkTreeIter iter;
GtkTreePath *p;
gboolean active;
EMailFormatterHeader *header;
gint *indices;
gtk_tree_model_get_iter_from_string (
GTK_TREE_MODEL (emp->priv->headers),
&iter, path);
gtk_tree_model_get (
GTK_TREE_MODEL (emp->priv->headers), &iter,
COLUMN_ACTIVE, &active, -1);
gtk_tree_model_get (
GTK_TREE_MODEL (emp->priv->headers), &iter,
COLUMN_HEADER_STRUCT, &header, -1);
gtk_list_store_set (
GTK_LIST_STORE (emp->priv->headers), &iter,
COLUMN_ACTIVE, !active, -1);
p = gtk_tree_path_new_from_string (path);
indices = gtk_tree_path_get_indices (p);
set_header_visible (emp, header, indices[0], !active);
gtk_tree_path_free (p);
}
static void
emp_headers_tab_toggle_selection (GtkWidget *button,
gpointer user_data)
{
EMailPrinter *emp = user_data;
GtkTreeIter iter;
gboolean select;
if (button == emp->priv->buttons[BUTTON_SELECT_ALL])
select = TRUE;
else if (button == emp->priv->buttons[BUTTON_SELECT_NONE])
select = FALSE;
else
return;
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (emp->priv->headers), &iter))
return;
do {
EMailFormatterHeader *header;
GtkTreePath *path;
gint *indices;
gtk_tree_model_get (
GTK_TREE_MODEL (emp->priv->headers), &iter,
COLUMN_HEADER_STRUCT, &header, -1);
gtk_list_store_set (
GTK_LIST_STORE (emp->priv->headers), &iter,
COLUMN_ACTIVE, select, -1);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (emp->priv->headers), &iter);
indices = gtk_tree_path_get_indices (path);
set_header_visible (emp, header, indices[0], select);
gtk_tree_path_free (path);
} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (emp->priv->headers), &iter));
}
static void
emp_headers_tab_selection_changed (GtkTreeSelection *selection,
gpointer user_data)
{
EMailPrinter *emp = user_data;
gboolean enabled;
GList *selected_rows;
GtkTreeIter iter;
GtkTreeModel *model;
GtkTreePath *path;
if (gtk_tree_selection_count_selected_rows (selection) == 0) {
gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_TOP], FALSE);
gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_UP], FALSE);
gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_DOWN], FALSE);
gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_BOTTOM], FALSE);
return;
}
model = GTK_TREE_MODEL (emp->priv->headers);
selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
path = gtk_tree_path_copy (selected_rows->data);
enabled = gtk_tree_path_prev (path);
gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_TOP], enabled);
gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_UP], enabled);
gtk_tree_model_get_iter (model, &iter, g_list_last (selected_rows)->data);
enabled = gtk_tree_model_iter_next (model, &iter);
gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_DOWN], enabled);
gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_BOTTOM], enabled);
g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
g_list_free (selected_rows);
gtk_tree_path_free (path);
}
static void
emp_headers_tab_move (GtkWidget *button,
gpointer user_data)
{
EMailPrinter *emp = user_data;
GtkTreeSelection *selection;
GList *selected_rows, *references, *l;
GtkTreePath *path;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeRowReference *selection_middle;
gint *indices;
WebKitDOMDocument *document;
WebKitDOMNodeList *headers;
WebKitDOMNode *header, *parent;
model = GTK_TREE_MODEL (emp->priv->headers);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (emp->priv->treeview));
selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
/* The order of header rows in the HMTL document should be in sync with
order of headers in the listview and in efhp->headers_list */
document = webkit_web_view_get_dom_document (emp->priv->webview);
headers = webkit_dom_document_get_elements_by_class_name (document, "header-item");
l = g_list_nth (selected_rows, g_list_length (selected_rows) / 2);
selection_middle = gtk_tree_row_reference_new (model, l->data);
references = NULL;
for (l = selected_rows; l; l = l->next) {
references = g_list_prepend (
references,
gtk_tree_row_reference_new (model, l->data));
}
if (button == emp->priv->buttons[BUTTON_TOP]) {
for (l = references; l; l = l->next) {
/* Move the rows in the view */
path = gtk_tree_row_reference_get_path (l->data);
gtk_tree_model_get_iter (model, &iter, path);
gtk_list_store_move_after (emp->priv->headers, &iter, NULL);
/* Move the header row in HTML document */
indices = gtk_tree_path_get_indices (path);
header = webkit_dom_node_list_item (headers, indices[0]);
parent = webkit_dom_node_get_parent_node (header);
webkit_dom_node_remove_child (parent, header, NULL);
webkit_dom_node_insert_before (parent, header,
webkit_dom_node_get_first_child (parent), NULL);
gtk_tree_path_free (path);
}
} else if (button == emp->priv->buttons[BUTTON_UP]) {
GtkTreeIter *iter_prev;
WebKitDOMNode *node2;
references = g_list_reverse (references);
for (l = references; l; l = l->next) {
path = gtk_tree_row_reference_get_path (l->data);
gtk_tree_model_get_iter (model, &iter, path);
iter_prev = gtk_tree_iter_copy (&iter);
gtk_tree_model_iter_previous (model, iter_prev);
gtk_list_store_move_before (emp->priv->headers, &iter, iter_prev);
indices = gtk_tree_path_get_indices (path);
header = webkit_dom_node_list_item (headers, indices[0]);
node2 = webkit_dom_node_get_previous_sibling (header);
parent = webkit_dom_node_get_parent_node (header);
webkit_dom_node_remove_child (parent, header, NULL);
webkit_dom_node_insert_before (parent, header, node2, NULL);
gtk_tree_path_free (path);
gtk_tree_iter_free (iter_prev);
}
} else if (button == emp->priv->buttons[BUTTON_DOWN]) {
GtkTreeIter *iter_next;
WebKitDOMNode *node2;
for (l = references; l; l = l->next) {
path = gtk_tree_row_reference_get_path (l->data);
gtk_tree_model_get_iter (model, &iter, path);
iter_next = gtk_tree_iter_copy (&iter);
gtk_tree_model_iter_next (model, iter_next);
gtk_list_store_move_after (emp->priv->headers, &iter, iter_next);
indices = gtk_tree_path_get_indices (path);
header = webkit_dom_node_list_item (headers, indices[0]);
node2 = webkit_dom_node_get_next_sibling (header);
parent = webkit_dom_node_get_parent_node (header);
webkit_dom_node_remove_child (parent, header, NULL);
webkit_dom_node_insert_before (parent, header,
webkit_dom_node_get_next_sibling (node2), NULL);
gtk_tree_path_free (path);
gtk_tree_iter_free (iter_next);
}
} else if (button == emp->priv->buttons[BUTTON_BOTTOM]) {
references = g_list_reverse (references);
for (l = references; l; l = l->next) {
path = gtk_tree_row_reference_get_path (l->data);
gtk_tree_model_get_iter (model, &iter, path);
gtk_list_store_move_before (emp->priv->headers, &iter, NULL);
/* Move the header row in HTML document */
indices = gtk_tree_path_get_indices (path);
header = webkit_dom_node_list_item (headers, indices[0]);
parent = webkit_dom_node_get_parent_node (header);
webkit_dom_node_remove_child (parent, header, NULL);
webkit_dom_node_append_child (parent, header, NULL);
gtk_tree_path_free (path);
}
};
g_list_foreach (references, (GFunc) gtk_tree_row_reference_free, NULL);
g_list_free (references);
/* Keep the selection in middle of the screen */
path = gtk_tree_row_reference_get_path (selection_middle);
gtk_tree_view_scroll_to_cell (
GTK_TREE_VIEW (emp->priv->treeview),
path, COLUMN_ACTIVE, TRUE, 0.5, 0.5);
gtk_tree_path_free (path);
gtk_tree_row_reference_free (selection_middle);
g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
g_list_free (selected_rows);
emp_headers_tab_selection_changed (selection, user_data);
}
static GtkWidget *
emp_create_headers_tab (GtkPrintOperation *operation,
EMailPrinter *emp)
{
GtkWidget *vbox, *hbox, *scw, *button;
GtkTreeView *view;
GtkTreeSelection *selection;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 5);
emp->priv->treeview = gtk_tree_view_new_with_model (
GTK_TREE_MODEL (emp->priv->headers));
view = GTK_TREE_VIEW (emp->priv->treeview);
selection = gtk_tree_view_get_selection (view);
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
g_signal_connect (
selection, "changed",
G_CALLBACK (emp_headers_tab_selection_changed), emp);
renderer = gtk_cell_renderer_toggle_new ();
g_signal_connect (
renderer, "toggled",
G_CALLBACK (header_active_renderer_toggled_cb), emp);
column = gtk_tree_view_column_new_with_attributes (
_("Print"), renderer,
"active", COLUMN_ACTIVE, NULL);
gtk_tree_view_append_column (view, column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (
_("Header Name"), renderer,
"text", COLUMN_HEADER_NAME, NULL);
gtk_tree_view_append_column (view, column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (
_("Header Value"), renderer,
"text", COLUMN_HEADER_VALUE, NULL);
gtk_tree_view_append_column (view, column);
scw = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (scw), GTK_WIDGET (view));
gtk_box_pack_start (GTK_BOX (hbox), scw, TRUE, TRUE, 0);
button = gtk_button_new_from_stock (GTK_STOCK_SELECT_ALL);
emp->priv->buttons[BUTTON_SELECT_ALL] = button;
g_signal_connect (
button, "clicked",
G_CALLBACK (emp_headers_tab_toggle_selection), emp);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
button = gtk_button_new_from_stock (GTK_STOCK_CLEAR);
emp->priv->buttons[BUTTON_SELECT_NONE] = button;
g_signal_connect (
button, "clicked",
G_CALLBACK (emp_headers_tab_toggle_selection), emp);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
button = gtk_button_new_from_stock (GTK_STOCK_GOTO_TOP);
emp->priv->buttons[BUTTON_TOP] = button;
gtk_widget_set_sensitive (button, FALSE);
g_signal_connect (
button, "clicked",
G_CALLBACK (emp_headers_tab_move), emp);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
button = gtk_button_new_from_stock (GTK_STOCK_GO_UP);
emp->priv->buttons[BUTTON_UP] = button;
gtk_widget_set_sensitive (button, FALSE);
g_signal_connect (
button, "clicked",
G_CALLBACK (emp_headers_tab_move), emp);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN);
emp->priv->buttons[BUTTON_DOWN] = button;
gtk_widget_set_sensitive (button, FALSE);
g_signal_connect (
button, "clicked",
G_CALLBACK (emp_headers_tab_move), emp);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
button = gtk_button_new_from_stock (GTK_STOCK_GOTO_BOTTOM);
emp->priv->buttons[BUTTON_BOTTOM] = button;
gtk_widget_set_sensitive (button, FALSE);
g_signal_connect (
button, "clicked",
G_CALLBACK (emp_headers_tab_move), emp);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
gtk_print_operation_set_custom_tab_label (operation, _("Headers"));
gtk_widget_show_all (hbox);
return hbox;
}
static gboolean
mail_printer_print_timeout_cb (gpointer user_data)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
GCancellable *cancellable;
GtkPrintOperation *print_operation;
GtkPrintOperationAction print_action;
EMailPrinter *printer;
WebKitWebFrame *web_frame;
gulong create_custom_widget_handler_id;
gulong draw_page_handler_id;
GError *error = NULL;
simple = G_SIMPLE_ASYNC_RESULT (user_data);
async_context = g_simple_async_result_get_op_res_gpointer (simple);
cancellable = async_context->cancellable;
print_action = async_context->print_action;
/* Check for cancellation one last time before printing. */
if (g_cancellable_set_error_if_cancelled (cancellable, &error))
goto exit;
/* This returns a new reference. */
printer = (EMailPrinter *) g_async_result_get_source_object (
G_ASYNC_RESULT (simple));
print_operation = e_print_operation_new ();
gtk_print_operation_set_show_progress (print_operation, TRUE);
gtk_print_operation_set_unit (print_operation, GTK_UNIT_PIXEL);
if (async_context->print_action == GTK_PRINT_OPERATION_ACTION_EXPORT) {
const gchar *export_filename;
export_filename =
e_mail_printer_get_export_filename (printer);
gtk_print_operation_set_export_filename (
print_operation, export_filename);
}
create_custom_widget_handler_id = g_signal_connect (
print_operation, "create-custom-widget",
G_CALLBACK (emp_create_headers_tab), printer);
draw_page_handler_id = g_signal_connect (
print_operation, "draw-page",
G_CALLBACK (mail_printer_draw_footer_cb),
async_context->cancellable);
web_frame = webkit_web_view_get_main_frame (async_context->web_view);
async_context->print_result = webkit_web_frame_print_full (
web_frame, print_operation, print_action, &error);
/* Sanity check. */
switch (async_context->print_result) {
case GTK_PRINT_OPERATION_RESULT_ERROR:
if (error == NULL)
g_warning (
"WebKit print operation returned "
"ERROR result without settings a "
"GError");
break;
case GTK_PRINT_OPERATION_RESULT_APPLY:
if (error != NULL)
g_warning (
"WebKit print operation returned "
"APPLY result but also set a GError");
case GTK_PRINT_OPERATION_RESULT_CANCEL:
if (error != NULL)
g_warning (
"WebKit print operation returned "
"CANCEL result but also set a GError");
break;
default:
g_warn_if_reached ();
}
g_signal_handler_disconnect (
print_operation, create_custom_widget_handler_id);
g_signal_handler_disconnect (
print_operation, draw_page_handler_id);
g_object_unref (print_operation);
g_object_unref (printer);
exit:
if (error != NULL)
g_simple_async_result_take_error (simple, error);
g_simple_async_result_complete_in_idle (simple);
return FALSE;
}
static void
mail_printer_load_status_cb (WebKitWebView *web_view,
GParamSpec *pspec,
GSimpleAsyncResult *simple)
{
AsyncContext *async_context;
WebKitLoadStatus load_status;
GCancellable *cancellable;
GError *error = NULL;
/* Note: we disregard WEBKIT_LOAD_FAILED and print what we can. */
load_status = webkit_web_view_get_load_status (web_view);
if (load_status != WEBKIT_LOAD_FINISHED)
return;
/* Signal handlers are holding the only GSimpleAsyncResult
* references. This is to avoid finalizing it prematurely. */
g_object_ref (simple);
async_context = g_simple_async_result_get_op_res_gpointer (simple);
cancellable = async_context->cancellable;
/* WebKit reloads the page once more right before starting to print,
* so disconnect this handler after the first time to avoid starting
* another print operation. */
g_signal_handler_disconnect (
async_context->web_view,
async_context->load_status_handler_id);
async_context->load_status_handler_id = 0;
/* Check if we've been cancelled. */
if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
g_simple_async_result_take_error (simple, error);
g_simple_async_result_complete_in_idle (simple);
/* Give WebKit some time to perform layouting and rendering before
* we start printing. 500ms should be enough in most cases. */
} else {
GSource *timeout_source;
timeout_source = g_timeout_source_new (500);
g_source_set_callback (
timeout_source,
mail_printer_print_timeout_cb,
g_object_ref (simple),
(GDestroyNotify) g_object_unref);
g_source_attach (
timeout_source, async_context->main_context);
g_source_unref (timeout_source);
}
g_object_unref (simple);
}
static void
mail_printer_build_model (EMailPrinter *printer,
EMailPartList *part_list)
{
CamelMediumHeader *header;
CamelMimeMessage *message;
GArray *headers;
GQueue *headers_queue;
gint i;
GtkTreeIter last_known = { 0 };
if (printer->priv->headers != NULL)
g_object_unref (printer->priv->headers);
printer->priv->headers = gtk_list_store_new (
5,
G_TYPE_BOOLEAN, /* COLUMN_ACTIVE */
G_TYPE_STRING, /* COLUMN_HEADER_NAME */
G_TYPE_STRING, /* COLUMN_HEADER_VALUE */
G_TYPE_POINTER, /* COLUMN_HEADER_STRUCT */
G_TYPE_INT); /* ??? */
message = e_mail_part_list_get_message (part_list);
headers = camel_medium_get_headers (CAMEL_MEDIUM (message));
if (!headers)
return;
headers_queue = e_mail_formatter_dup_headers (printer->priv->formatter);
for (i = 0; i < headers->len; i++) {
GtkTreeIter iter;
GList *found_header;
EMailFormatterHeader *emfh;
header = &g_array_index (headers, CamelMediumHeader, i);
emfh = e_mail_formatter_header_new (header->name, header->value);
found_header = g_queue_find_custom (
headers_queue, emfh, (GCompareFunc)
mail_printer_header_name_equal);
if (!found_header) {
emfh->flags |= E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN;
e_mail_formatter_add_header_struct (
printer->priv->formatter, emfh);
gtk_list_store_append (printer->priv->headers, &iter);
} else {
if (gtk_list_store_iter_is_valid (printer->priv->headers, &last_known))
gtk_list_store_insert_after (printer->priv->headers, &iter, &last_known);
else
gtk_list_store_insert_after (printer->priv->headers, &iter, NULL);
last_known = iter;
}
gtk_list_store_set (
printer->priv->headers, &iter,
COLUMN_ACTIVE, (found_header != NULL),
COLUMN_HEADER_NAME, emfh->name,
COLUMN_HEADER_VALUE, emfh->value,
COLUMN_HEADER_STRUCT, emfh, -1);
}
g_queue_free_full (
headers_queue,
(GDestroyNotify) e_mail_formatter_header_free);
camel_medium_free_headers (CAMEL_MEDIUM (message), headers);
}
static WebKitWebView *
mail_printer_new_web_view (const gchar *charset,
const gchar *default_charset)
{
WebKitWebView *web_view;
EMailFormatter *formatter;
web_view = g_object_new (
E_TYPE_MAIL_DISPLAY,
"mode", E_MAIL_FORMATTER_MODE_PRINTING, NULL);
e_web_view_set_enable_frame_flattening (E_WEB_VIEW (web_view), FALSE);
e_mail_display_set_force_load_images (E_MAIL_DISPLAY (web_view), TRUE);
formatter = e_mail_display_get_formatter (E_MAIL_DISPLAY (web_view));
if (charset != NULL && *charset != '\0')
e_mail_formatter_set_charset (formatter, charset);
if (default_charset != NULL && *default_charset != '\0')
e_mail_formatter_set_default_charset (formatter, default_charset);
return web_view;
}
static void
mail_printer_set_part_list (EMailPrinter *printer,
EMailPartList *part_list)
{
g_return_if_fail (E_IS_MAIL_PART_LIST (part_list));
g_return_if_fail (printer->priv->part_list == NULL);
printer->priv->part_list = g_object_ref (part_list);
}
static void
mail_printer_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_PART_LIST:
mail_printer_set_part_list (
E_MAIL_PRINTER (object),
g_value_get_object (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
mail_printer_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_PART_LIST:
g_value_take_object (
value,
e_mail_printer_ref_part_list (
E_MAIL_PRINTER (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
mail_printer_dispose (GObject *object)
{
EMailPrinterPrivate *priv;
priv = E_MAIL_PRINTER_GET_PRIVATE (object);
if (priv->headers != NULL) {
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
model = GTK_TREE_MODEL (priv->headers);
valid = gtk_tree_model_get_iter_first (model, &iter);
while (valid) {
EMailFormatterHeader *header = NULL;
gtk_tree_model_get (
model, &iter,
COLUMN_HEADER_STRUCT, &header, -1);
e_mail_formatter_header_free (header);
valid = gtk_tree_model_iter_next (model, &iter);
}
}
g_clear_object (&priv->formatter);
g_clear_object (&priv->part_list);
g_clear_object (&priv->headers);
g_clear_object (&priv->webview);
g_clear_object (&priv->operation);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_mail_printer_parent_class)->dispose (object);
}
static void
mail_printer_finalize (GObject *object)
{
EMailPrinterPrivate *priv;
priv = E_MAIL_PRINTER_GET_PRIVATE (object);
g_free (priv->uri);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_mail_printer_parent_class)->finalize (object);
}
static void
mail_printer_constructed (GObject *object)
{
EMailPrinter *printer;
EMailPartList *part_list;
printer = E_MAIL_PRINTER (object);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_mail_printer_parent_class)->constructed (object);
part_list = e_mail_printer_ref_part_list (printer);
mail_printer_build_model (printer, part_list);
g_object_unref (part_list);
}
static void
e_mail_printer_class_init (EMailPrinterClass *class)
{
GObjectClass *object_class;
g_type_class_add_private (class, sizeof (EMailPrinterPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = mail_printer_set_property;
object_class->get_property = mail_printer_get_property;
object_class->dispose = mail_printer_dispose;
object_class->finalize = mail_printer_finalize;
object_class->constructed = mail_printer_constructed;
g_object_class_install_property (
object_class,
PROP_PART_LIST,
g_param_spec_object (
"part-list",
"Part List",
NULL,
E_TYPE_MAIL_PART_LIST,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
e_mail_printer_init (EMailPrinter *printer)
{
printer->priv = E_MAIL_PRINTER_GET_PRIVATE (printer);
printer->priv->formatter = e_mail_formatter_print_new ();
}
EMailPrinter *
e_mail_printer_new (EMailPartList *part_list)
{
g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL);
return g_object_new (
E_TYPE_MAIL_PRINTER,
"part-list", part_list, NULL);
}
EMailPartList *
e_mail_printer_ref_part_list (EMailPrinter *printer)
{
g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL);
return g_object_ref (printer->priv->part_list);
}
void
e_mail_printer_print (EMailPrinter *printer,
GtkPrintOperationAction action,
EMailFormatter *formatter,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
WebKitWebView *web_view;
EMailPartList *part_list;
CamelFolder *folder;
const gchar *message_uid;
const gchar *charset = NULL;
const gchar *default_charset = NULL;
gchar *mail_uri;
gulong handler_id;
g_return_if_fail (E_IS_MAIL_PRINTER (printer));
/* EMailFormatter can be NULL. */
async_context = g_slice_new0 (AsyncContext);
async_context->print_action = action;
async_context->main_context = g_main_context_ref_thread_default ();
if (G_IS_CANCELLABLE (cancellable))
async_context->cancellable = g_object_ref (cancellable);
part_list = e_mail_printer_ref_part_list (printer);
folder = e_mail_part_list_get_folder (part_list);
message_uid = e_mail_part_list_get_message_uid (part_list);
if (formatter != NULL) {
charset =
e_mail_formatter_get_charset (formatter);
default_charset =
e_mail_formatter_get_default_charset (formatter);
}
if (charset == NULL)
charset = "";
if (default_charset == NULL)
default_charset = "";
simple = g_simple_async_result_new (
G_OBJECT (printer), callback,
user_data, e_mail_printer_print);
g_simple_async_result_set_check_cancellable (simple, cancellable);
g_simple_async_result_set_op_res_gpointer (
simple, async_context, (GDestroyNotify) async_context_free);
web_view = mail_printer_new_web_view (charset, default_charset);
e_mail_display_set_parts_list (E_MAIL_DISPLAY (web_view), part_list);
async_context->web_view = g_object_ref_sink (web_view);
handler_id = g_signal_connect_data (
web_view, "notify::load-status",
G_CALLBACK (mail_printer_load_status_cb),
g_object_ref (simple),
(GClosureNotify) g_object_unref, 0);
async_context->load_status_handler_id = handler_id;
mail_uri = e_mail_part_build_uri (
folder, message_uid,
"__evo-load-image", G_TYPE_BOOLEAN, TRUE,
"mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_PRINTING,
"formatter_default_charset", G_TYPE_STRING, default_charset,
"formatter_charset", G_TYPE_STRING, charset,
NULL);
webkit_web_view_load_uri (web_view, mail_uri);
g_free (mail_uri);
g_object_unref (simple);
g_object_unref (part_list);
}
GtkPrintOperationResult
e_mail_printer_print_finish (EMailPrinter *printer,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
g_return_val_if_fail (
g_simple_async_result_is_valid (
result, G_OBJECT (printer), e_mail_printer_print),
GTK_PRINT_OPERATION_RESULT_ERROR);
simple = G_SIMPLE_ASYNC_RESULT (result);
async_context = g_simple_async_result_get_op_res_gpointer (simple);
if (g_simple_async_result_propagate_error (simple, error))
return GTK_PRINT_OPERATION_RESULT_ERROR;
g_warn_if_fail (
async_context->print_result !=
GTK_PRINT_OPERATION_RESULT_ERROR);
return async_context->print_result;
}
const gchar *
e_mail_printer_get_export_filename (EMailPrinter *printer)
{
g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL);
return printer->priv->export_filename;
}
void
e_mail_printer_set_export_filename (EMailPrinter *printer,
const gchar *filename)
{
g_return_if_fail (E_IS_MAIL_PRINTER (printer));
g_free (printer->priv->export_filename);
printer->priv->export_filename = g_strdup (filename);
}