From 27c92e7f3d166e0ab38ad8d652b3c5079a86cddb Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Sat, 11 May 2013 16:05:22 -0400 Subject: Add EMailPrintConfigHeaders. This splits the print dialog's "Headers" tab into a separate widget. EMailPrintConfigHeaders takes an EMailPartHeaders and displays its print model, which is a representation of all message headers (except subject) with an on/off flag for each. The headers can be toggled and reordered, and the changes are written back to the print model. During printing, EMailFormatterPrintHeaders uses the same print model to determine which headers to show and in what order (except subject). This approach is much saner than the old method, which was trying to manipulate WebKitWebView DOM directly to toggle and reorder headers. This approach also happens to work, whereas the old method did not. --- mail/e-mail-printer.c | 542 ++++---------------------------------------------- 1 file changed, 44 insertions(+), 498 deletions(-) (limited to 'mail/e-mail-printer.c') diff --git a/mail/e-mail-printer.c b/mail/e-mail-printer.c index 2d5af9159d..68ee6ba65b 100644 --- a/mail/e-mail-printer.c +++ b/mail/e-mail-printer.c @@ -33,6 +33,7 @@ #include "e-mail-printer.h" #include "e-mail-display.h" +#include "e-mail-print-config-headers.h" #define w(x) @@ -58,8 +59,6 @@ struct _EMailPrinterPrivate { gchar *export_filename; - GtkListStore *headers; - WebKitWebView *webview; /* WebView to print from */ gchar *uri; GtkWidget *buttons[BUTTONS_COUNT]; @@ -114,19 +113,38 @@ async_context_free (AsyncContext *async_context) g_slice_free (AsyncContext, async_context); } -static gint -mail_printer_header_name_equal (const EMailFormatterHeader *h1, - const EMailFormatterHeader *h2) +static GtkWidget * +mail_printer_create_custom_widget_cb (GtkPrintOperation *operation, + AsyncContext *async_context) { - 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; - } + EMailDisplay *display; + EMailPartList *part_list; + EMailPart *part; + GtkWidget *widget; + + gtk_print_operation_set_custom_tab_label (operation, _("Headers")); + + display = E_MAIL_DISPLAY (async_context->web_view); + part_list = e_mail_display_get_parts_list (display); + + /* FIXME Hard-coding the part ID works for now but could easily + * break silently. Need a less brittle way of extracting + * specific parts by either MIME type or GType. */ + part = e_mail_part_list_ref_part (part_list, ".message.headers"); + + widget = e_mail_print_config_headers_new (E_MAIL_PART_HEADERS (part)); + + g_object_unref (part); + + return widget; +} + +static void +mail_printer_custom_widget_apply_cb (GtkPrintOperation *operation, + GtkWidget *widget, + AsyncContext *async_context) +{ + webkit_web_view_reload (async_context->web_view); } static void @@ -166,383 +184,6 @@ mail_printer_draw_footer_cb (GtkPrintOperation *operation, 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) { @@ -554,6 +195,7 @@ mail_printer_print_timeout_cb (gpointer user_data) EMailPrinter *printer; WebKitWebFrame *web_frame; gulong create_custom_widget_handler_id; + gulong custom_widget_apply_handler_id; gulong draw_page_handler_id; GError *error = NULL; @@ -572,6 +214,7 @@ mail_printer_print_timeout_cb (gpointer user_data) 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); @@ -586,7 +229,13 @@ mail_printer_print_timeout_cb (gpointer user_data) create_custom_widget_handler_id = g_signal_connect ( print_operation, "create-custom-widget", - G_CALLBACK (emp_create_headers_tab), printer); + G_CALLBACK (mail_printer_create_custom_widget_cb), + async_context); + + custom_widget_apply_handler_id = g_signal_connect ( + print_operation, "custom-widget-apply", + G_CALLBACK (mail_printer_custom_widget_apply_cb), + async_context); draw_page_handler_id = g_signal_connect ( print_operation, "draw-page", @@ -626,6 +275,9 @@ mail_printer_print_timeout_cb (gpointer user_data) g_signal_handler_disconnect ( print_operation, create_custom_widget_handler_id); + g_signal_handler_disconnect ( + print_operation, custom_widget_apply_handler_id); + g_signal_handler_disconnect ( print_operation, draw_page_handler_id); @@ -696,74 +348,6 @@ mail_printer_load_status_cb (WebKitWebView *web_view, 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) @@ -838,29 +422,8 @@ mail_printer_dispose (GObject *object) 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); @@ -881,22 +444,6 @@ mail_printer_finalize (GObject *object) 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) { @@ -909,7 +456,6 @@ e_mail_printer_class_init (EMailPrinterClass *class) 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, -- cgit