evolution-3.6.4/mail/e-mail-printer.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-printer.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-printer.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  * This program is free software; you can redistribute it and/or
  3  * modify it under the terms of the GNU Lesser General Public
  4  * License as published by the Free Software Foundation; either
  5  * version 2 of the License, or (at your option) version 3.
  6  *
  7  * This program is distributed in the hope that it will be useful,
  8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 10  * Lesser General Public License for more details.
 11  *
 12  * You should have received a copy of the GNU Lesser General Public
 13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 14  *
 15  * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com>
 16  *
 17  */
 18 
 19 #ifdef HAVE_CONFIG_H
 20 #include <config.h>
 21 #endif
 22 
 23 #include <string.h>
 24 #include <glib/gi18n.h>
 25 #include <gtk/gtk.h>
 26 
 27 #include <em-format/e-mail-formatter-print.h>
 28 #include <em-format/e-mail-part-utils.h>
 29 
 30 #include <e-util/e-print.h>
 31 #include <e-util/e-marshal.h>
 32 
 33 #include <webkit/webkitdom.h>
 34 
 35 #include "e-mail-printer.h"
 36 #include "e-mail-display.h"
 37 
 38 enum {
 39         BUTTON_SELECT_ALL,
 40         BUTTON_SELECT_NONE,
 41         BUTTON_TOP,
 42         BUTTON_UP,
 43         BUTTON_DOWN,
 44         BUTTON_BOTTOM,
 45         BUTTONS_COUNT
 46 };
 47 
 48 #define w(x)
 49 
 50 struct _EMailPrinterPrivate {
 51 	EMailFormatterPrint *formatter;
 52 	EMailPartList *parts_list;
 53 
 54 	gchar *export_filename;
 55 
 56 	GtkListStore *headers;
 57 
 58 	WebKitWebView *webview; /* WebView to print from */
 59 	gchar *uri;
 60 	GtkWidget *buttons[BUTTONS_COUNT];
 61 	GtkWidget *treeview;
 62 
 63 	GtkPrintOperation *operation;
 64 	GtkPrintOperationAction print_action;
 65 };
 66 
 67 G_DEFINE_TYPE (
 68 	EMailPrinter,
 69 	e_mail_printer,
 70 	G_TYPE_OBJECT);
 71 
 72 enum {
 73 	PROP_0,
 74 	PROP_PART_LIST
 75 };
 76 
 77 enum {
 78 	SIGNAL_DONE,
 79 	LAST_SIGNAL
 80 };
 81 
 82 enum {
 83 	COLUMN_ACTIVE,
 84 	COLUMN_HEADER_NAME,
 85 	COLUMN_HEADER_VALUE,
 86 	COLUMN_HEADER_STRUCT,
 87 	LAST_COLUMN
 88 };
 89 
 90 static guint signals[LAST_SIGNAL];
 91 
 92 static gint
 93 emp_header_name_equal (const EMailFormatterHeader *h1,
 94                        const EMailFormatterHeader *h2)
 95 {
 96 	if ((h2->value == NULL) || (h1->value == NULL)) {
 97 		return g_strcmp0 (h1->name, h2->name);
 98 	} else {
 99 		if ((g_strcmp0 (h1->name, h2->name) == 0) &&
100 		    (g_strcmp0 (h1->value, h2->value) == 0))
101 			return 0;
102 		else
103 			return 1;
104 	}
105 }
106 
107 static void
108 emp_draw_footer (GtkPrintOperation *operation,
109                  GtkPrintContext *context,
110                  gint page_nr)
111 {
112 	PangoFontDescription *desc;
113 	PangoLayout *layout;
114 	gint n_pages;
115 	gdouble width, height;
116 	gchar *text;
117 	cairo_t *cr;
118 
119 	cr = gtk_print_context_get_cairo_context (context);
120 	width = gtk_print_context_get_width (context);
121 	height = gtk_print_context_get_height (context);
122 
123 	g_object_get (operation, "n-pages", &n_pages, NULL);
124 	text = g_strdup_printf (_("Page %d of %d"), page_nr + 1, n_pages);
125 
126 	cairo_set_source_rgb (cr, 0.1, 0.1, 0.1);
127 	cairo_fill (cr);
128 
129 	desc = pango_font_description_from_string ("Sans Regular 10");
130 	layout = gtk_print_context_create_pango_layout (context);
131 	pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
132 	pango_layout_set_font_description (layout, desc);
133 	pango_layout_set_text (layout, text, -1);
134 	pango_layout_set_width (layout, width * PANGO_SCALE);
135 	pango_font_description_free (desc);
136 
137 	cairo_move_to (cr, 0, height + 5);
138 	pango_cairo_show_layout (cr, layout);
139 
140 	g_object_unref (layout);
141 	g_free (text);
142 }
143 
144 static void
145 emp_printing_done (GtkPrintOperation *operation,
146                    GtkPrintOperationResult result,
147                    gpointer user_data)
148 {
149 	EMailPrinter *emp = user_data;
150 
151 	g_signal_emit (emp, signals[SIGNAL_DONE], 0, operation, result);
152 }
153 
154 static gboolean
155 do_run_print_operation (EMailPrinter *emp)
156 {
157 	WebKitWebFrame *frame;
158 
159 	frame = webkit_web_view_get_main_frame (emp->priv->webview);
160 
161 	webkit_web_frame_print_full (
162 		frame, emp->priv->operation, emp->priv->print_action, NULL);
163 
164 	return FALSE;
165 }
166 
167 static void
168 emp_start_printing (GObject *object,
169                     GParamSpec *pspec,
170                     gpointer user_data)
171 {
172 	WebKitWebView *web_view;
173 	WebKitLoadStatus load_status;
174 	EMailPrinter *emp = user_data;
175 
176 	web_view = WEBKIT_WEB_VIEW (object);
177 	load_status = webkit_web_view_get_load_status (web_view);
178 
179 	if (load_status != WEBKIT_LOAD_FINISHED)
180 		return;
181 
182 	/* WebKit reloads the page once more right before starting to print, so
183 	 * disconnect this handler after the first time, so that we don't start
184 	 * another printing operation */
185 	g_signal_handlers_disconnect_by_func (
186 		object, emp_start_printing, user_data);
187 
188 	if (emp->priv->print_action == GTK_PRINT_OPERATION_ACTION_EXPORT) {
189 		gtk_print_operation_set_export_filename (
190 			emp->priv->operation, emp->priv->export_filename);
191 	}
192 
193 	/* Give WebKit some time to perform layouting and rendering before
194 	 * we start printing. 500ms should be enough in most cases... */
195 	g_timeout_add_full (
196 		G_PRIORITY_DEFAULT, 500,
197 		(GSourceFunc) do_run_print_operation,
198 		g_object_ref (emp), g_object_unref);
199 }
200 
201 static void
202 emp_run_print_operation (EMailPrinter *emp,
203 			 EMailFormatter *formatter)
204 {
205 	gchar *mail_uri;
206 	const gchar *default_charset, *charset;
207 
208 	default_charset = formatter ? e_mail_formatter_get_default_charset (formatter) : NULL;
209 	charset = formatter ? e_mail_formatter_get_charset (formatter) : NULL;
210 
211 	if (!default_charset)
212 		default_charset = "";
213 	if (!charset)
214 		charset = "";
215 
216 	mail_uri = e_mail_part_build_uri (
217 		emp->priv->parts_list->folder,
218 		emp->priv->parts_list->message_uid,
219 		"__evo-load-image", G_TYPE_BOOLEAN, TRUE,
220 		"mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_PRINTING,
221 		"formatter_default_charset", G_TYPE_STRING, default_charset,
222 		"formatter_charset", G_TYPE_STRING, charset,
223 		NULL);
224 
225 	/* Print_layout is a special EMPart created by EMFormatHTMLPrint */
226 	if (emp->priv->webview == NULL) {
227 		EMailFormatter *emp_formatter;
228 
229 		emp->priv->webview = g_object_new (
230 			E_TYPE_MAIL_DISPLAY,
231 			"mode", E_MAIL_FORMATTER_MODE_PRINTING, NULL);
232 		e_web_view_set_enable_frame_flattening (E_WEB_VIEW (emp->priv->webview), FALSE);
233 		e_mail_display_set_force_load_images (
234 			E_MAIL_DISPLAY (emp->priv->webview), TRUE);
235 
236 		emp_formatter = e_mail_display_get_formatter (E_MAIL_DISPLAY (emp->priv->webview));
237 		if (default_charset && *default_charset)
238 			e_mail_formatter_set_default_charset (emp_formatter, default_charset);
239 		if (charset && *charset)
240 			e_mail_formatter_set_charset (emp_formatter, charset);
241 
242 		g_object_ref_sink (emp->priv->webview);
243 		g_signal_connect (
244 			emp->priv->webview, "notify::load-status",
245 			G_CALLBACK (emp_start_printing), emp);
246 
247 		w ({
248 			GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
249 			GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
250 			gtk_container_add (GTK_CONTAINER (window), sw);
251 			gtk_container_add (
252 				GTK_CONTAINER (sw),
253 				GTK_WIDGET (emp->priv->webview));
254 			gtk_widget_show_all (window);
255 		});
256 	}
257 	e_mail_display_set_parts_list (
258 		E_MAIL_DISPLAY (emp->priv->webview), emp->priv->parts_list);
259 	webkit_web_view_load_uri (emp->priv->webview, mail_uri);
260 
261 	g_free (mail_uri);
262 }
263 
264 static void
265 set_header_visible (EMailPrinter *emp,
266                     EMailFormatterHeader *header,
267                     gint index,
268                     gboolean visible)
269 {
270 	WebKitDOMDocument *document;
271 	WebKitDOMNodeList *headers;
272 	WebKitDOMElement *element;
273 	WebKitDOMCSSStyleDeclaration *style;
274 
275 	document = webkit_web_view_get_dom_document (emp->priv->webview);
276 	headers = webkit_dom_document_get_elements_by_class_name (document, "header-item");
277 
278 	g_return_if_fail (index < webkit_dom_node_list_get_length (headers));
279 
280 	element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (headers, index));
281 	style = webkit_dom_element_get_style (element);
282 	webkit_dom_css_style_declaration_set_property (
283 		style,
284 		"display", (visible ? "table-row" : "none"), "", NULL);
285 }
286 
287 static void
288 header_active_renderer_toggled_cb (GtkCellRendererToggle *renderer,
289                                    gchar *path,
290                                    EMailPrinter *emp)
291 {
292 	GtkTreeIter iter;
293 	GtkTreePath *p;
294 	gboolean active;
295 	EMailFormatterHeader *header;
296 	gint *indices;
297 
298 	gtk_tree_model_get_iter_from_string (
299 		GTK_TREE_MODEL (emp->priv->headers),
300 		&iter, path);
301 
302 	gtk_tree_model_get (
303 		GTK_TREE_MODEL (emp->priv->headers), &iter,
304 		COLUMN_ACTIVE, &active, -1);
305 	gtk_tree_model_get (
306 		GTK_TREE_MODEL (emp->priv->headers), &iter,
307 		COLUMN_HEADER_STRUCT, &header, -1);
308 	gtk_list_store_set (
309 		GTK_LIST_STORE (emp->priv->headers), &iter,
310 		COLUMN_ACTIVE, !active, -1);
311 
312 	p = gtk_tree_path_new_from_string (path);
313 	indices = gtk_tree_path_get_indices (p);
314 	set_header_visible (emp, header, indices[0], !active);
315 	gtk_tree_path_free (p);
316 }
317 
318 static void
319 emp_headers_tab_toggle_selection (GtkWidget *button,
320                                   gpointer user_data)
321 {
322 	EMailPrinter *emp = user_data;
323 	GtkTreeIter iter;
324 	gboolean select;
325 
326 	if (button == emp->priv->buttons[BUTTON_SELECT_ALL])
327 		select = TRUE;
328 	else if (button == emp->priv->buttons[BUTTON_SELECT_NONE])
329 		select = FALSE;
330 	else
331 		return;
332 
333 	if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (emp->priv->headers), &iter))
334 		return;
335 
336 	do {
337 		EMailFormatterHeader *header;
338 		GtkTreePath *path;
339 		gint *indices;
340 
341 		gtk_tree_model_get (
342 			GTK_TREE_MODEL (emp->priv->headers), &iter,
343 			COLUMN_HEADER_STRUCT, &header, -1);
344 		gtk_list_store_set (
345 			GTK_LIST_STORE (emp->priv->headers), &iter,
346 			COLUMN_ACTIVE, select, -1);
347 
348 		path = gtk_tree_model_get_path (GTK_TREE_MODEL (emp->priv->headers), &iter);
349 		indices = gtk_tree_path_get_indices (path);
350 		set_header_visible (emp, header, indices[0], select);
351 		gtk_tree_path_free (path);
352 
353 	} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (emp->priv->headers), &iter));
354 }
355 
356 static void
357 emp_headers_tab_selection_changed (GtkTreeSelection *selection,
358                                    gpointer user_data)
359 {
360 	EMailPrinter *emp = user_data;
361 	gboolean enabled;
362 	GList *selected_rows;
363 	GtkTreeIter iter;
364 	GtkTreeModel *model;
365 	GtkTreePath *path;
366 
367 	if (gtk_tree_selection_count_selected_rows (selection) == 0) {
368 		gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_TOP], FALSE);
369 		gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_UP], FALSE);
370 		gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_DOWN], FALSE);
371 		gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_BOTTOM], FALSE);
372 
373 		return;
374 	}
375 
376 	model = GTK_TREE_MODEL (emp->priv->headers);
377 	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
378 
379 	path = gtk_tree_path_copy (selected_rows->data);
380 	enabled = gtk_tree_path_prev (path);
381 	gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_TOP], enabled);
382 	gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_UP], enabled);
383 
384 	gtk_tree_model_get_iter (model, &iter, g_list_last (selected_rows)->data);
385 	enabled = gtk_tree_model_iter_next (model, &iter);
386 	gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_DOWN], enabled);
387 	gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_BOTTOM], enabled);
388 
389 	g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
390 	g_list_free (selected_rows);
391 	gtk_tree_path_free (path);
392 }
393 
394 static void
395 emp_headers_tab_move (GtkWidget *button,
396                       gpointer user_data)
397 {
398 	EMailPrinter *emp = user_data;
399 	GtkTreeSelection *selection;
400 	GList *selected_rows, *references, *l;
401 	GtkTreePath *path;
402 	GtkTreeModel *model;
403 	GtkTreeIter iter;
404 	GtkTreeRowReference *selection_middle;
405 	gint *indices;
406 
407 	WebKitDOMDocument *document;
408 	WebKitDOMNodeList *headers;
409 	WebKitDOMNode *header, *parent;
410 
411 	model = GTK_TREE_MODEL (emp->priv->headers);
412 	selection = gtk_tree_view_get_selection  (GTK_TREE_VIEW (emp->priv->treeview));
413 	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
414 
415         /* The order of header rows in the HMTL document should be in sync with
416 	   order of headers in the listview and in efhp->headers_list */
417 	document = webkit_web_view_get_dom_document (emp->priv->webview);
418 	headers = webkit_dom_document_get_elements_by_class_name (document, "header-item");
419 
420 	l = g_list_nth (selected_rows, g_list_length (selected_rows) / 2);
421 	selection_middle = gtk_tree_row_reference_new (model, l->data);
422 
423 	references = NULL;
424 	for (l = selected_rows; l; l = l->next) {
425 		references = g_list_prepend (
426 			references,
427 			gtk_tree_row_reference_new (model, l->data));
428 	}
429 
430 	if (button == emp->priv->buttons[BUTTON_TOP]) {
431 
432 		for (l = references; l; l = l->next) {
433                         /* Move the rows in the view  */
434 			path = gtk_tree_row_reference_get_path (l->data);
435 			gtk_tree_model_get_iter (model, &iter, path);
436 			gtk_list_store_move_after (emp->priv->headers, &iter, NULL);
437 
438                         /* Move the header row in HTML document */
439 			indices = gtk_tree_path_get_indices (path);
440 			header = webkit_dom_node_list_item (headers, indices[0]);
441 			parent = webkit_dom_node_get_parent_node (header);
442 			webkit_dom_node_remove_child (parent, header, NULL);
443 			webkit_dom_node_insert_before (parent, header,
444 				webkit_dom_node_get_first_child (parent), NULL);
445 
446 			gtk_tree_path_free (path);
447 		}
448 
449        } else if (button == emp->priv->buttons[BUTTON_UP]) {
450 
451 		GtkTreeIter *iter_prev;
452 		WebKitDOMNode *node2;
453 
454 		references = g_list_reverse (references);
455 
456 		for (l = references; l; l = l->next) {
457 
458 			path = gtk_tree_row_reference_get_path (l->data);
459 			gtk_tree_model_get_iter (model, &iter, path);
460 			iter_prev = gtk_tree_iter_copy (&iter);
461 			gtk_tree_model_iter_previous (model, iter_prev);
462 
463 			gtk_list_store_move_before (emp->priv->headers, &iter, iter_prev);
464 
465 			indices = gtk_tree_path_get_indices (path);
466 			header = webkit_dom_node_list_item (headers, indices[0]);
467 			node2 = webkit_dom_node_get_previous_sibling (header);
468 			parent = webkit_dom_node_get_parent_node (header);
469 
470 			webkit_dom_node_remove_child (parent, header, NULL);
471 			webkit_dom_node_insert_before (parent, header, node2, NULL);
472 
473 			gtk_tree_path_free (path);
474 			gtk_tree_iter_free (iter_prev);
475 		}
476 
477 	} else if (button == emp->priv->buttons[BUTTON_DOWN]) {
478 
479 		GtkTreeIter *iter_next;
480 		WebKitDOMNode *node2;
481 
482 		for (l = references; l; l = l->next) {
483 
484 			path = gtk_tree_row_reference_get_path (l->data);
485 			gtk_tree_model_get_iter (model, &iter, path);
486 			iter_next = gtk_tree_iter_copy (&iter);
487 			gtk_tree_model_iter_next (model, iter_next);
488 
489 			gtk_list_store_move_after (emp->priv->headers, &iter, iter_next);
490 
491 			indices = gtk_tree_path_get_indices (path);
492 			header = webkit_dom_node_list_item (headers, indices[0]);
493 			node2 = webkit_dom_node_get_next_sibling (header);
494 			parent = webkit_dom_node_get_parent_node (header);
495 
496 			webkit_dom_node_remove_child (parent, header, NULL);
497 			webkit_dom_node_insert_before (parent, header,
498 				webkit_dom_node_get_next_sibling (node2), NULL);
499 
500 			gtk_tree_path_free (path);
501 			gtk_tree_iter_free (iter_next);
502 		}
503 
504 	} else if (button == emp->priv->buttons[BUTTON_BOTTOM]) {
505 
506 		references = g_list_reverse (references);
507 
508 		for (l = references; l; l = l->next) {
509 			path = gtk_tree_row_reference_get_path (l->data);
510 			gtk_tree_model_get_iter (model, &iter, path);
511 			gtk_list_store_move_before (emp->priv->headers, &iter, NULL);
512 
513                         /* Move the header row in HTML document */
514 			indices = gtk_tree_path_get_indices (path);
515 			header = webkit_dom_node_list_item (headers, indices[0]);
516 			parent = webkit_dom_node_get_parent_node (header);
517 			webkit_dom_node_remove_child (parent, header, NULL);
518 			webkit_dom_node_append_child (parent, header, NULL);
519 
520 			gtk_tree_path_free (path);
521 		}
522 	};
523 
524 	g_list_foreach (references, (GFunc) gtk_tree_row_reference_free, NULL);
525 	g_list_free (references);
526 
527         /* Keep the selection in middle of the screen */
528 	path = gtk_tree_row_reference_get_path (selection_middle);
529 	gtk_tree_view_scroll_to_cell (
530 		GTK_TREE_VIEW (emp->priv->treeview),
531 		path, COLUMN_ACTIVE, TRUE, 0.5, 0.5);
532 	gtk_tree_path_free (path);
533 	gtk_tree_row_reference_free (selection_middle);
534 
535 	g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
536 	g_list_free (selected_rows);
537 
538 	emp_headers_tab_selection_changed (selection, user_data);
539 }
540 
541 static GtkWidget *
542 emp_create_headers_tab (GtkPrintOperation *operation,
543                         EMailPrinter *emp)
544 {
545 	GtkWidget *vbox, *hbox, *scw, *button;
546 	GtkTreeView *view;
547 	GtkTreeSelection *selection;
548 	GtkTreeViewColumn *column;
549 	GtkCellRenderer *renderer;
550 
551 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
552 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
553 	gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 5);
554 
555 	emp->priv->treeview = gtk_tree_view_new_with_model (
556 		GTK_TREE_MODEL (emp->priv->headers));
557 	view = GTK_TREE_VIEW (emp->priv->treeview);
558 	selection = gtk_tree_view_get_selection (view);
559 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
560 	g_signal_connect (
561 		selection, "changed",
562 		G_CALLBACK (emp_headers_tab_selection_changed), emp);
563 
564 	renderer = gtk_cell_renderer_toggle_new ();
565 	g_signal_connect (
566 		renderer, "toggled",
567 		G_CALLBACK (header_active_renderer_toggled_cb), emp);
568 	column = gtk_tree_view_column_new_with_attributes (
569 		_("Print"), renderer,
570 		"active", COLUMN_ACTIVE, NULL);
571 	gtk_tree_view_append_column (view, column);
572 
573 	renderer = gtk_cell_renderer_text_new ();
574 	column = gtk_tree_view_column_new_with_attributes (
575 		_("Header Name"), renderer,
576 		"text", COLUMN_HEADER_NAME, NULL);
577 	gtk_tree_view_append_column (view, column);
578 
579 	renderer = gtk_cell_renderer_text_new ();
580 	column = gtk_tree_view_column_new_with_attributes (
581 		_("Header Value"), renderer,
582 		"text", COLUMN_HEADER_VALUE, NULL);
583 	gtk_tree_view_append_column (view, column);
584 
585 	scw = gtk_scrolled_window_new (NULL, NULL);
586 	gtk_container_add (GTK_CONTAINER (scw), GTK_WIDGET (view));
587 	gtk_box_pack_start (GTK_BOX (hbox), scw, TRUE, TRUE, 0);
588 
589 	button = gtk_button_new_from_stock (GTK_STOCK_SELECT_ALL);
590 	emp->priv->buttons[BUTTON_SELECT_ALL] = button;
591 	g_signal_connect (
592 		button, "clicked",
593 		G_CALLBACK (emp_headers_tab_toggle_selection), emp);
594 	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
595 
596 	button = gtk_button_new_from_stock (GTK_STOCK_CLEAR);
597 	emp->priv->buttons[BUTTON_SELECT_NONE] = button;
598 	g_signal_connect (
599 		button, "clicked",
600 		G_CALLBACK (emp_headers_tab_toggle_selection), emp);
601 	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
602 
603 	button = gtk_button_new_from_stock (GTK_STOCK_GOTO_TOP);
604 	emp->priv->buttons[BUTTON_TOP] = button;
605 	gtk_widget_set_sensitive (button, FALSE);
606 	g_signal_connect (
607 		button, "clicked",
608 		G_CALLBACK (emp_headers_tab_move), emp);
609 	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
610 
611 	button = gtk_button_new_from_stock (GTK_STOCK_GO_UP);
612 	emp->priv->buttons[BUTTON_UP] = button;
613 	gtk_widget_set_sensitive (button, FALSE);
614 	g_signal_connect (
615 		button, "clicked",
616 		G_CALLBACK (emp_headers_tab_move), emp);
617 	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
618 
619 	button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN);
620 	emp->priv->buttons[BUTTON_DOWN] = button;
621 	gtk_widget_set_sensitive (button, FALSE);
622 	g_signal_connect (
623 		button, "clicked",
624 		G_CALLBACK (emp_headers_tab_move), emp);
625 	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
626 
627 	button = gtk_button_new_from_stock (GTK_STOCK_GOTO_BOTTOM);
628 	emp->priv->buttons[BUTTON_BOTTOM] = button;
629 	gtk_widget_set_sensitive (button, FALSE);
630 	g_signal_connect (
631 		button, "clicked",
632 		G_CALLBACK (emp_headers_tab_move), emp);
633 	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
634 
635 	gtk_print_operation_set_custom_tab_label (operation, _("Headers"));
636 	gtk_widget_show_all (hbox);
637 
638 	return hbox;
639 }
640 
641 static void
642 emp_set_parts_list (EMailPrinter *emp,
643                     EMailPartList *parts_list)
644 {
645 	CamelMediumHeader *header;
646 	GArray *headers;
647 	gint i;
648 	GtkTreeIter last_known = { 0 };
649 
650 	g_return_if_fail (parts_list);
651 
652 	emp->priv->parts_list = g_object_ref (parts_list);
653 
654 	if (emp->priv->headers)
655 		g_object_unref (emp->priv->headers);
656 	emp->priv->headers = gtk_list_store_new (
657 		5,
658 		G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
659 
660 	headers = camel_medium_get_headers (CAMEL_MEDIUM (parts_list->message));
661 	if (!headers)
662 		return;
663 
664 	for (i = 0; i < headers->len; i++) {
665 		GtkTreeIter iter;
666 		GList *found_header;
667 		EMailFormatterHeader *emfh;
668 
669 		header = &g_array_index (headers, CamelMediumHeader, i);
670 		emfh = e_mail_formatter_header_new (header->name, header->value);
671 
672 		found_header = g_queue_find_custom (
673 				(GQueue *) e_mail_formatter_get_headers (
674 					E_MAIL_FORMATTER (emp->priv->formatter)),
675 				emfh, (GCompareFunc) emp_header_name_equal);
676 
677 		if (!found_header) {
678 			emfh->flags |= E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN;
679 			e_mail_formatter_add_header_struct (
680 				E_MAIL_FORMATTER (emp->priv->formatter), emfh);
681 			gtk_list_store_append (emp->priv->headers, &iter);
682 		} else {
683 			if (gtk_list_store_iter_is_valid (emp->priv->headers, &last_known))
684 				gtk_list_store_insert_after (emp->priv->headers, &iter, &last_known);
685 			else
686 				gtk_list_store_insert_after (emp->priv->headers, &iter, NULL);
687 
688 			last_known = iter;
689 		}
690 
691 		gtk_list_store_set (
692 			emp->priv->headers, &iter,
693 			COLUMN_ACTIVE, (found_header != NULL),
694 			COLUMN_HEADER_NAME, emfh->name,
695 			COLUMN_HEADER_VALUE, emfh->value,
696 			COLUMN_HEADER_STRUCT, emfh, -1);
697 	}
698 
699 	camel_medium_free_headers (CAMEL_MEDIUM (parts_list->message), headers);
700 }
701 
702 static void
703 emp_set_property (GObject *object,
704                   guint property_id,
705                   const GValue *value,
706                   GParamSpec *pspec)
707 {
708 	EMailPrinter *emp = E_MAIL_PRINTER (object);
709 
710 	switch (property_id) {
711 
712 		case PROP_PART_LIST:
713 			emp_set_parts_list (emp, g_value_get_pointer (value));
714 			return;
715 	}
716 
717 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
718 }
719 
720 static void
721 emp_get_property (GObject *object,
722                   guint property_id,
723                   GValue *value,
724                   GParamSpec *pspec)
725 {
726 	EMailPrinter *emp = E_MAIL_PRINTER (object);
727 
728 	switch (property_id) {
729 
730 		case PROP_PART_LIST:
731 			g_value_set_pointer (value, emp->priv->parts_list);
732 			return;
733 	}
734 
735 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
736 }
737 
738 static void
739 emp_finalize (GObject *object)
740 {
741 	EMailPrinterPrivate *priv = E_MAIL_PRINTER (object)->priv;
742 
743 	if (priv->formatter) {
744 		g_object_unref (priv->formatter);
745 		priv->formatter = NULL;
746 	}
747 
748 	if (priv->headers) {
749 		GtkTreeIter iter;
750 
751 		if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->headers), &iter)) {
752 			do {
753 				EMailFormatterHeader *header = NULL;
754 				gtk_tree_model_get (
755 					GTK_TREE_MODEL (priv->headers), &iter,
756 					COLUMN_HEADER_STRUCT, &header, -1);
757 				e_mail_formatter_header_free (header);
758 			} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->headers), &iter));
759 		}
760 		g_object_unref (priv->headers);
761 		priv->headers = NULL;
762 	}
763 
764 	if (priv->webview) {
765 		g_object_unref (priv->webview);
766 		priv->webview = NULL;
767 	}
768 
769 	if (priv->uri) {
770 		g_free (priv->uri);
771 		priv->uri = NULL;
772 	}
773 
774 	if (priv->operation) {
775 		g_object_unref (priv->operation);
776 		priv->operation = NULL;
777 	}
778 
779 	if (priv->parts_list) {
780 		g_object_unref (priv->parts_list);
781 		priv->parts_list = NULL;
782 	}
783 
784         /* Chain up to parent's finalize() method. */
785 	G_OBJECT_CLASS (e_mail_printer_parent_class)->finalize (object);
786 }
787 
788 static void
789 e_mail_printer_class_init (EMailPrinterClass *class)
790 {
791 	GObjectClass *object_class;
792 
793 	g_type_class_add_private (class, sizeof (EMailPrinterPrivate));
794 
795 	object_class = G_OBJECT_CLASS (class);
796 	object_class->set_property = emp_set_property;
797 	object_class->get_property = emp_get_property;
798 	object_class->finalize = emp_finalize;
799 
800 	g_object_class_install_property (
801 		object_class,
802 		PROP_PART_LIST,
803 		g_param_spec_pointer (
804 			"parts-list",
805 			"Parts List",
806 			NULL,
807 			G_PARAM_READWRITE |
808 			G_PARAM_CONSTRUCT_ONLY));
809 
810 	signals[SIGNAL_DONE] = g_signal_new (
811 		"done",
812 		G_TYPE_FROM_CLASS (class),
813 		G_SIGNAL_RUN_FIRST,
814 		G_STRUCT_OFFSET (EMailPrinterClass, done),
815 		NULL, NULL,
816 		e_marshal_VOID__OBJECT_INT,
817 		G_TYPE_NONE, 2,
818 		GTK_TYPE_PRINT_OPERATION, G_TYPE_INT);
819 }
820 
821 static void
822 e_mail_printer_init (EMailPrinter *emp)
823 {
824 	emp->priv = G_TYPE_INSTANCE_GET_PRIVATE (
825 		emp, E_TYPE_MAIL_PRINTER, EMailPrinterPrivate);
826 
827 	emp->priv->formatter = (EMailFormatterPrint *) e_mail_formatter_print_new ();
828 	emp->priv->headers = NULL;
829 	emp->priv->webview = NULL;
830 }
831 
832 EMailPrinter *
833 e_mail_printer_new (EMailPartList *source)
834 {
835 	EMailPrinter *emp;
836 
837 	emp = g_object_new (
838 		E_TYPE_MAIL_PRINTER,
839 		"parts-list", source, NULL);
840 
841 	return emp;
842 }
843 
844 void
845 e_mail_printer_print (EMailPrinter *emp,
846                       GtkPrintOperationAction action,
847 		      EMailFormatter *formatter,
848                       GCancellable *cancellable)
849 {
850 	g_return_if_fail (E_IS_MAIL_PRINTER (emp));
851 
852 	if (emp->priv->operation)
853 		g_object_unref (emp->priv->operation);
854 	emp->priv->operation = e_print_operation_new ();
855 	emp->priv->print_action = action;
856 	gtk_print_operation_set_unit (emp->priv->operation, GTK_UNIT_PIXEL);
857 
858 	gtk_print_operation_set_show_progress (emp->priv->operation, TRUE);
859 	g_signal_connect (
860 		emp->priv->operation, "create-custom-widget",
861 		G_CALLBACK (emp_create_headers_tab), emp);
862 	g_signal_connect (
863 		emp->priv->operation, "done",
864 		G_CALLBACK (emp_printing_done), emp);
865 	g_signal_connect (
866 		emp->priv->operation, "draw-page",
867 		G_CALLBACK (emp_draw_footer), NULL);
868 
869 	if (cancellable)
870 		g_signal_connect_swapped (
871 			cancellable, "cancelled",
872 			G_CALLBACK (gtk_print_operation_cancel), emp->priv->operation);
873 
874 	emp_run_print_operation (emp, formatter);
875 }
876 
877 const gchar *
878 e_mail_printer_get_export_filename (EMailPrinter *printer)
879 {
880 	g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL);
881 
882 	return printer->priv->export_filename;
883 }
884 
885 void
886 e_mail_printer_set_export_filename (EMailPrinter *printer,
887                                     const gchar *filename)
888 {
889 	g_return_if_fail (E_IS_MAIL_PRINTER (printer));
890 
891 	if (printer->priv->export_filename)
892 	  g_free (printer->priv->export_filename);
893 
894 	printer->priv->export_filename = g_strdup (filename);
895 }