diff options
| author | Tomas Popela <tpopela@redhat.com> | 2016-08-11 08:13:00 (GMT) |
|---|---|---|
| committer | Milan Crha <mcrha@redhat.com> | 2016-08-11 08:13:00 (GMT) |
| commit | 332789ff89d85f8302ae53d6abe2cd993cd28eea (patch) | |
| tree | b4367aa3dba067b190ea11f2d59ba5c32e04416d | |
| parent | 76c34f7d98bef3e31bba6fd77f491ce03aa7c0f8 (diff) | |
| download | evolution-332789f.zip evolution-332789f.tar.xz | |
Bug 751588 - Port to WebKit2
214 files changed, 59038 insertions, 38043 deletions
diff --git a/Makefile.am b/Makefile.am index 8d65a34..addfd44 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,6 +71,7 @@ SUBDIRS = \ mail \ calendar \ art \ + web-extensions \ plugins \ modules \ $(MAINT_SUBDIR) \ diff --git a/addressbook/gui/contact-editor/e-contact-editor.c b/addressbook/gui/contact-editor/e-contact-editor.c index 488fc32..4704528 100644 --- a/addressbook/gui/contact-editor/e-contact-editor.c +++ b/addressbook/gui/contact-editor/e-contact-editor.c @@ -995,7 +995,7 @@ fill_in_email (EContactEditor *editor) email_location = eab_get_email_type_index (attr); slot = get_ui_slot (attr); if (slot < 1) - slot = EMAIL_SLOTS + 1; //add at the end + slot = EMAIL_SLOTS + 1; /* add at the end */ gtk_list_store_append (data_store, &iter); gtk_list_store_set (data_store, &iter, @@ -2103,7 +2103,7 @@ fill_in_im (EContactEditor *editor) slot = get_ui_slot (attr); if (slot < 0) - slot = IM_SLOTS + 1; //attach at the end + slot = IM_SLOTS + 1; /* attach at the end */ gtk_list_store_append (data_store, &iter); gtk_list_store_set (data_store, &iter, diff --git a/addressbook/gui/widgets/eab-contact-display.c b/addressbook/gui/widgets/eab-contact-display.c index cd7fafd..8378563 100644 --- a/addressbook/gui/widgets/eab-contact-display.c +++ b/addressbook/gui/widgets/eab-contact-display.c @@ -28,7 +28,7 @@ #include <string.h> #include <glib/gi18n.h> -#include <webkit/webkit.h> +#include <webkit2/webkit2.h> #include "e-contact-map.h" #include "eab-contact-formatter.h" @@ -386,19 +386,31 @@ contact_display_object_requested (WebKitWebView *web_view, #endif static void -contact_display_load_status_changed (WebKitWebView *web_view, - GParamSpec *pspec, - gpointer user_data) +contact_display_load_changed (WebKitWebView *web_view, + WebKitLoadEvent load_event, + gpointer user_data) { - WebKitLoadStatus load_status; - WebKitDOMDocument *document; + GDBusProxy *web_extension; + GVariant* result; - load_status = webkit_web_view_get_load_status (web_view); - if (load_status != WEBKIT_LOAD_FINISHED) + if (load_event != WEBKIT_LOAD_FINISHED) return; - document = webkit_web_view_get_dom_document (web_view); - eab_contact_formatter_bind_dom (document); + web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (web_view)); + if (web_extension) { + result = g_dbus_proxy_call_sync ( + web_extension, + "EABContactFormatterBindDOM", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id (web_view)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + NULL); + if (result) + g_variant_unref (result); + } } static void @@ -514,15 +526,12 @@ eab_contact_display_init (EABContactDisplay *display) G_CALLBACK (contact_display_object_requested), display); #endif e_signal_connect_notify ( - web_view, "notify::load-status", - G_CALLBACK (contact_display_load_status_changed), NULL); + web_view, "notify::load-changed", + G_CALLBACK (contact_display_load_changed), NULL); g_signal_connect ( web_view, "style-updated", G_CALLBACK (load_contact), NULL); - e_web_view_install_request_handler (E_WEB_VIEW (display), E_TYPE_FILE_REQUEST); - e_web_view_install_request_handler (E_WEB_VIEW (display), E_TYPE_STOCK_REQUEST); - action_group = gtk_action_group_new ("internal-mailto"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); diff --git a/addressbook/gui/widgets/eab-contact-formatter.c b/addressbook/gui/widgets/eab-contact-formatter.c index 4869811..d2e217b 100644 --- a/addressbook/gui/widgets/eab-contact-formatter.c +++ b/addressbook/gui/widgets/eab-contact-formatter.c @@ -1383,64 +1383,3 @@ eab_contact_formatter_format_contact (EABContactFormatter *formatter, else render_compact (formatter, contact, output_buffer); } - -static void -collapse_contacts_list (WebKitDOMEventTarget *event_target, - WebKitDOMEvent *event, - gpointer user_data) -{ - WebKitDOMDocument *document; - WebKitDOMElement *list; - gchar *id, *list_id; - gchar *imagesdir, *src; - gboolean hidden; - - document = user_data; - id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (event_target)); - - list_id = g_strconcat ("list-", id, NULL); - list = webkit_dom_document_get_element_by_id (document, list_id); - g_free (id); - g_free (list_id); - - if (list == NULL) - return; - - imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); - hidden = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (list)); - - if (hidden) - src = g_strdup_printf ("evo-file://%s/minus.png", imagesdir); - else - src = g_strdup_printf ("evo-file://%s/plus.png", imagesdir); - - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (list), !hidden); - webkit_dom_html_image_element_set_src ( - WEBKIT_DOM_HTML_IMAGE_ELEMENT (event_target), src); - - g_free (src); - g_free (imagesdir); -} - -void -eab_contact_formatter_bind_dom (WebKitDOMDocument *document) -{ - WebKitDOMNodeList *nodes; - gulong ii, length; - - nodes = webkit_dom_document_get_elements_by_class_name ( - document, "_evo_collapse_button"); - - length = webkit_dom_node_list_get_length (nodes); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (nodes, ii); - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (node), "click", - G_CALLBACK (collapse_contacts_list), FALSE, document); - } - - g_object_unref (nodes); -} diff --git a/addressbook/gui/widgets/eab-contact-formatter.h b/addressbook/gui/widgets/eab-contact-formatter.h index b4acc62..b691f31 100644 --- a/addressbook/gui/widgets/eab-contact-formatter.h +++ b/addressbook/gui/widgets/eab-contact-formatter.h @@ -75,7 +75,6 @@ void eab_contact_formatter_format_contact (EABContactFormatter *formatter, EContact *contact, GString *output_buffer); -void eab_contact_formatter_bind_dom (WebKitDOMDocument *document); G_END_DECLS diff --git a/calendar/gui/e-comp-editor-page-attachments.c b/calendar/gui/e-comp-editor-page-attachments.c index 776f3a2..5910c7a 100644 --- a/calendar/gui/e-comp-editor-page-attachments.c +++ b/calendar/gui/e-comp-editor-page-attachments.c @@ -177,17 +177,7 @@ ecep_attachments_attachment_loaded_cb (EAttachment *attachment, } if (!e_attachment_load_finish (attachment, result, &error)) { - GtkTreeRowReference *reference; - - reference = e_attachment_get_reference (attachment); - if (gtk_tree_row_reference_valid (reference)) { - GtkTreeModel *model; - - model = gtk_tree_row_reference_get_model (reference); - - e_attachment_store_remove_attachment ( - E_ATTACHMENT_STORE (model), attachment); - } + g_signal_emit_by_name (attachment, "load-failed", NULL); /* Ignore cancellations. */ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { diff --git a/calendar/gui/itip-utils.c b/calendar/gui/itip-utils.c index 5a688b0..c873e99 100644 --- a/calendar/gui/itip-utils.c +++ b/calendar/gui/itip-utils.c @@ -1639,17 +1639,18 @@ find_enabled_identity (ESourceRegistry *registry, return mail_identity; } -static void -setup_from (ECalComponentItipMethod method, - ECalComponent *comp, - ECalClient *cal_client, - EComposerHeaderTable *table) +static gchar * +get_identity_uid_for_from (EShell *shell, + ECalComponentItipMethod method, + ECalComponent *comp, + ECalClient *cal_client) { EClientCache *client_cache; ESourceRegistry *registry; ESource *source = NULL; + gchar *identity_uid = NULL; - client_cache = e_composer_header_table_ref_client_cache (table); + client_cache = e_shell_get_client_cache (shell); registry = e_client_cache_ref_registry (client_cache); /* always use organizer's email when user is an organizer */ @@ -1673,16 +1674,14 @@ setup_from (ECalComponentItipMethod method, } if (source != NULL) { - const gchar *uid; - - uid = e_source_get_uid (source); - e_composer_header_table_set_identity_uid (table, uid); + identity_uid = g_strdup (e_source_get_uid (source)); g_object_unref (source); } - g_object_unref (client_cache); g_object_unref (registry); + + return identity_uid; } typedef struct { @@ -1771,83 +1770,64 @@ itip_send_component_begin (ItipSendComponentData *isc, } } +typedef struct _CreateComposerData { + gchar *identity_uid; + EDestination **destinations; + gchar *subject; + gchar *ical_string; + gchar *content_type; + gchar *event_body_text; + GSList *attachments_list; + ECalComponent *comp; + gboolean show_only; +} CreateComposerData; + static void -itip_send_component_complete (ItipSendComponentData *isc) +itip_send_component_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - GSettings *settings; - EMsgComposer *composer; + CreateComposerData *ccd = user_data; EComposerHeaderTable *table; - EDestination **destinations; - ECalComponent *comp = NULL; - icalcomponent *top_level = NULL; - icaltimezone *default_zone; - gchar *ical_string = NULL; - gchar *content_type = NULL; - gchar *subject = NULL; + EMsgComposer *composer; + GSettings *settings; gboolean use_24hour_format; + GError *error = NULL; - g_return_if_fail (isc != NULL); + g_return_if_fail (ccd != NULL); - if (isc->completed) + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); return; - - isc->success = FALSE; + } settings = e_util_ref_settings ("org.gnome.evolution.calendar"); use_24hour_format = g_settings_get_boolean (settings, "use-24hour-format"); g_object_unref (settings); - default_zone = calendar_config_get_icaltimezone (); - - /* Tidy up the comp */ - comp = comp_compliant ( - isc->registry, isc->method, isc->send_comp, isc->cal_client, - isc->zones, default_zone, isc->strip_alarms); - - if (comp == NULL) - goto cleanup; - - /* Recipients */ - destinations = comp_to_list ( - isc->registry, isc->method, comp, isc->users, FALSE, - isc->only_new_attendees ? g_object_get_data ( - G_OBJECT (isc->send_comp), "new-attendees") : NULL); - if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH) { - if (destinations == NULL) { - /* We sent them all via the server */ - isc->success = TRUE; - goto cleanup; - } - } - - /* Subject information */ - subject = comp_subject (isc->registry, isc->method, comp); - - composer = e_msg_composer_new (e_shell_get_default ()); table = e_msg_composer_get_header_table (composer); - setup_from (isc->method, isc->send_comp, isc->cal_client, table); - e_composer_header_table_set_subject (table, subject); - e_composer_header_table_set_destinations_to (table, destinations); - - e_destination_freev (destinations); + if (ccd->identity_uid) + e_composer_header_table_set_identity_uid (table, ccd->identity_uid); - /* Content type */ - content_type = comp_content_type (comp, isc->method); + e_composer_header_table_set_subject (table, ccd->subject); + e_composer_header_table_set_destinations_to (table, ccd->destinations); - top_level = comp_toplevel_with_zones (isc->method, comp, isc->cal_client, isc->zones); - ical_string = icalcomponent_as_ical_string_r (top_level); - - if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) { - e_msg_composer_set_body (composer, ical_string, content_type); + if (e_cal_component_get_vtype (ccd->comp) == E_CAL_COMPONENT_EVENT) { + if (ccd->event_body_text) + e_msg_composer_set_body_text (composer, ccd->event_body_text, TRUE); + else + e_msg_composer_set_body (composer, ccd->ical_string, ccd->content_type); } else { CamelMimePart *attachment; const gchar *filename; gchar *description; gchar *body; - filename = comp_filename (comp); - description = comp_description (comp, use_24hour_format); + filename = comp_filename (ccd->comp); + description = comp_description (ccd->comp, use_24hour_format); body = camel_text_to_html (description, CAMEL_MIME_FILTER_TOHTML_PRE, 0); e_msg_composer_set_body_text (composer, body, TRUE); @@ -1855,8 +1835,8 @@ itip_send_component_complete (ItipSendComponentData *isc) attachment = camel_mime_part_new (); camel_mime_part_set_content ( - attachment, ical_string, - strlen (ical_string), content_type); + attachment, ccd->ical_string, + strlen (ccd->ical_string), ccd->content_type); if (filename != NULL && *filename != '\0') camel_mime_part_set_filename (attachment, filename); if (description != NULL && *description != '\0') @@ -1868,23 +1848,89 @@ itip_send_component_complete (ItipSendComponentData *isc) g_free (description); } - append_cal_attachments (composer, comp, isc->attachments_list); - isc->attachments_list = NULL; + append_cal_attachments (composer, ccd->comp, ccd->attachments_list); + ccd->attachments_list = NULL; - if (isc->method == E_CAL_COMPONENT_METHOD_PUBLISH && !isc->users) + if (ccd->show_only) gtk_widget_show (GTK_WIDGET (composer)); else e_msg_composer_send (composer); + e_destination_freev (ccd->destinations); + g_clear_object (&ccd->comp); + g_free (ccd->identity_uid); + g_free (ccd->subject); + g_free (ccd->ical_string); + g_free (ccd->content_type); + g_free (ccd->event_body_text); + g_free (ccd); +} + +static void +itip_send_component_complete (ItipSendComponentData *isc) +{ + CreateComposerData *ccd; + EDestination **destinations; + ECalComponent *comp = NULL; + EShell *shell; + icalcomponent *top_level = NULL; + icaltimezone *default_zone; + + g_return_if_fail (isc != NULL); + + if (isc->completed) + return; + + isc->success = FALSE; + + default_zone = calendar_config_get_icaltimezone (); + + /* Tidy up the comp */ + comp = comp_compliant ( + isc->registry, isc->method, isc->send_comp, isc->cal_client, + isc->zones, default_zone, isc->strip_alarms); + + if (comp == NULL) + goto cleanup; + + /* Recipients */ + destinations = comp_to_list ( + isc->registry, isc->method, comp, isc->users, FALSE, + isc->only_new_attendees ? g_object_get_data ( + G_OBJECT (isc->send_comp), "new-attendees") : NULL); + if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH) { + if (destinations == NULL) { + /* We sent them all via the server */ + isc->success = TRUE; + goto cleanup; + } + } + + shell = e_shell_get_default (); + top_level = comp_toplevel_with_zones (isc->method, comp, isc->cal_client, isc->zones); + + ccd = g_new0 (CreateComposerData, 1); + ccd->identity_uid = get_identity_uid_for_from (shell, isc->method, isc->send_comp, isc->cal_client); + ccd->destinations = destinations; + ccd->subject = comp_subject (isc->registry, isc->method, comp); + ccd->ical_string = icalcomponent_as_ical_string_r (top_level); + ccd->content_type = comp_content_type (comp, isc->method); + ccd->event_body_text = NULL; + ccd->attachments_list = isc->attachments_list; + ccd->comp = comp; + ccd->show_only = isc->method == E_CAL_COMPONENT_METHOD_PUBLISH && !isc->users; + + isc->attachments_list = NULL; + comp = NULL; + + e_msg_composer_new (shell, itip_send_component_composer_created_cb, ccd); + isc->success = TRUE; cleanup: g_clear_object (&comp); if (top_level != NULL) icalcomponent_free (top_level); - g_free (content_type); - g_free (subject); - g_free (ical_string); } static void @@ -2119,15 +2165,11 @@ reply_to_calendar_comp (ESourceRegistry *registry, GSList *attachments_list) { EShell *shell; - EMsgComposer *composer; - EComposerHeaderTable *table; - EDestination **destinations; ECalComponent *comp = NULL; icalcomponent *top_level = NULL; icaltimezone *default_zone; - gchar *subject = NULL; - gchar *ical_string = NULL; gboolean retval = FALSE; + CreateComposerData *ccd; g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE); @@ -2143,24 +2185,15 @@ reply_to_calendar_comp (ESourceRegistry *registry, if (comp == NULL) goto cleanup; - /* Recipients */ - destinations = comp_to_list ( - registry, method, comp, NULL, reply_all, NULL); - - /* Subject information */ - subject = comp_subject (registry, method, comp); - - composer = e_msg_composer_new (shell); - table = e_msg_composer_get_header_table (composer); - - setup_from (method, send_comp, cal_client, table); - e_composer_header_table_set_subject (table, subject); - e_composer_header_table_set_destinations_to (table, destinations); - - e_destination_freev (destinations); - top_level = comp_toplevel_with_zones (method, comp, cal_client, zones); - ical_string = icalcomponent_as_ical_string_r (top_level); + + ccd = g_new0 (CreateComposerData, 1); + ccd->identity_uid = get_identity_uid_for_from (shell, method, send_comp, cal_client); + ccd->destinations = comp_to_list (registry, method, comp, NULL, reply_all, NULL); + ccd->subject = comp_subject (registry, method, comp); + ccd->ical_string = icalcomponent_as_ical_string_r (top_level); + ccd->comp = comp; + ccd->show_only = TRUE; if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) { @@ -2268,11 +2301,12 @@ reply_to_calendar_comp (ESourceRegistry *registry, g_string_append (body, html_description); g_free (html_description); - e_msg_composer_set_body_text (composer, body->str, TRUE); - g_string_free (body, TRUE); + ccd->event_body_text = g_string_free (body, FALSE); } - gtk_widget_show (GTK_WIDGET (composer)); + comp = NULL; + + e_msg_composer_new (shell, itip_send_component_composer_created_cb, ccd); retval = TRUE; @@ -2283,8 +2317,6 @@ reply_to_calendar_comp (ESourceRegistry *registry, if (top_level != NULL) icalcomponent_free (top_level); - g_free (subject); - g_free (ical_string); return retval; } diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c index 5995ef0..77e1b88 100644 --- a/composer/e-composer-actions.c +++ b/composer/e-composer-actions.c @@ -65,40 +65,57 @@ action_close_cb (GtkAction *action, } static void +action_new_message_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + EMsgComposer *composer; + GError *error = NULL; + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + gtk_widget_show (GTK_WIDGET (composer)); + } +} + +static void action_new_message_cb (GtkAction *action, EMsgComposer *composer) { - EMsgComposer *new_composer; EShell *shell; shell = e_msg_composer_get_shell (composer); - new_composer = e_msg_composer_new (shell); - gtk_widget_show (GTK_WIDGET (new_composer)); + e_msg_composer_new (shell, action_new_message_composer_created_cb, NULL); } static void -action_pgp_encrypt_cb (GtkToggleAction *action, - EMsgComposer *composer) +composer_set_content_editor_changed (EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_changed (cnt_editor, TRUE); + +} + +static void +action_pgp_encrypt_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + composer_set_content_editor_changed (composer); } static void action_pgp_sign_cb (GtkToggleAction *action, EMsgComposer *composer) { - EHTMLEditor *editor; - EHTMLEditorView *view; - - editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); + composer_set_content_editor_changed (composer); } static void @@ -150,7 +167,6 @@ action_save_cb (GtkAction *action, EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; const gchar *filename; gint fd; GError *error = NULL; @@ -195,8 +211,7 @@ action_save_cb (GtkAction *action, return; } - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); + composer_set_content_editor_changed (composer); } static void @@ -256,37 +271,14 @@ static void action_smime_encrypt_cb (GtkToggleAction *action, EMsgComposer *composer) { - EHTMLEditor *editor; - EHTMLEditorView *view; - - editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); + composer_set_content_editor_changed (composer); } static void action_smime_sign_cb (GtkToggleAction *action, EMsgComposer *composer) { - EHTMLEditor *editor; - EHTMLEditorView *view; - - editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); -} - -static void -action_unicode_smileys_cb (GtkToggleAction *action, - EMsgComposer *composer) -{ - EHTMLEditor *editor; - EHTMLEditorView *view; - - editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_unicode_smileys (view, - gtk_toggle_action_get_active (action)); + composer_set_content_editor_changed (composer); } static void @@ -520,10 +512,10 @@ static GtkToggleActionEntry toggle_entries[] = { { "unicode-smileys", NULL, - N_("Unicode emoticons"), + N_("Unicode smilyes"), NULL, - N_("Use Unicode characters for emoticons."), - G_CALLBACK (action_unicode_smileys_cb), + N_("Use Unicode characters for smileys."), + NULL, /* Handled by property bindings */ FALSE }, { "view-bcc", @@ -566,14 +558,14 @@ e_composer_actions_init (EMsgComposer *composer) GtkAccelGroup *accel_group; GtkUIManager *ui_manager; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; gboolean visible; GIcon *gcr_gnupg_icon; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); ui_manager = e_html_editor_get_ui_manager (editor); /* Composer Actions */ @@ -678,32 +670,32 @@ e_composer_actions_init (EMsgComposer *composer) } e_binding_bind_property ( - view, "html-mode", + cnt_editor, "html-mode", ACTION (PICTURE_GALLERY), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", e_html_editor_get_action (editor, "edit-menu"), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", e_html_editor_get_action (editor, "format-menu"), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", e_html_editor_get_action (editor, "insert-menu"), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", e_html_editor_get_action (editor, "options-menu"), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", e_html_editor_get_action (editor, "picture-gallery"), "sensitive", G_BINDING_SYNC_CREATE); diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c index 65b8dd5..2b20b28 100644 --- a/composer/e-composer-private.c +++ b/composer/e-composer-private.c @@ -64,14 +64,14 @@ static void composer_update_gallery_visibility (EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GtkToggleAction *toggle_action; gboolean gallery_active; gboolean is_html; editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - is_html = e_html_editor_view_get_html_mode (view); + cnt_editor = e_html_editor_get_content_editor (editor); + is_html = e_content_editor_get_html_mode (cnt_editor); toggle_action = GTK_TOGGLE_ACTION (ACTION (PICTURE_GALLERY)); gallery_active = gtk_toggle_action_get_active (toggle_action); @@ -94,7 +94,7 @@ e_composer_private_constructed (EMsgComposer *composer) EShell *shell; EClientCache *client_cache; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GtkUIManager *ui_manager; GtkAction *action; GtkWidget *container; @@ -109,7 +109,7 @@ e_composer_private_constructed (EMsgComposer *composer) editor = e_msg_composer_get_editor (composer); ui_manager = e_html_editor_get_ui_manager (editor); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); settings = e_util_ref_settings ("org.gnome.evolution.mail"); @@ -136,10 +136,9 @@ e_composer_private_constructed (EMsgComposer *composer) priv->saved_editable = FALSE; priv->drop_occured = FALSE; priv->dnd_is_uri = FALSE; + priv->dnd_history_saved = FALSE; priv->check_if_signature_is_changed = FALSE; priv->ignore_next_signature_change = FALSE; - priv->dnd_history_saved = FALSE; - priv->ignore_next_paste_clipboard_signals_emission = FALSE; priv->focused_entry = NULL; @@ -214,7 +213,7 @@ e_composer_private_constructed (EMsgComposer *composer) E_COMPOSER_HEADER_TABLE (widget), E_COMPOSER_HEADER_SUBJECT); e_binding_bind_property ( - view, "spell-checker", + cnt_editor, "spell-checker", header->input_widget, "spell-checker", G_BINDING_SYNC_CREATE); @@ -233,7 +232,7 @@ e_composer_private_constructed (EMsgComposer *composer) gtk_widget_show (widget); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", widget, "sensitive", G_BINDING_SYNC_CREATE); @@ -255,11 +254,12 @@ e_composer_private_constructed (EMsgComposer *composer) priv->gallery_scrolled_window = g_object_ref (widget); gtk_widget_show (widget); - /* Reparent the scrolled window containing the web view - * widget into the content area of the top attachment pane. */ - - widget = GTK_WIDGET (view); - widget = gtk_widget_get_parent (widget); + widget = GTK_WIDGET (cnt_editor); + if (GTK_IS_SCROLLABLE (cnt_editor)) { + /* Scrollables are packed in a scrolled window */ + widget = gtk_widget_get_parent (widget); + g_warn_if_fail (GTK_IS_SCROLLED_WINDOW (widget)); + } gtk_widget_reparent (widget, container); /* Construct the picture gallery. */ @@ -275,7 +275,7 @@ e_composer_private_constructed (EMsgComposer *composer) g_free (gallery_path); e_signal_connect_notify_swapped ( - view, "notify::html-mode", + cnt_editor, "notify::html-mode", G_CALLBACK (composer_update_gallery_visibility), composer); g_signal_connect_swapped ( @@ -290,7 +290,6 @@ e_composer_private_constructed (EMsgComposer *composer) for (ii = 0; ii < E_COMPOSER_NUM_HEADERS; ii++) { EComposerHeaderTable *table; EComposerHeader *header; - GtkAction *action; table = E_COMPOSER_HEADER_TABLE (priv->header_table); header = e_composer_header_table_get_header (table, ii); @@ -520,6 +519,7 @@ e_composer_paste_image (EMsgComposer *composer, e_attachment_load_handle_error, composer); g_object_unref (attachment); + g_free (uri); return TRUE; @@ -670,7 +670,7 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box, gboolean is_html; GError *error = NULL; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; e_mail_signature_combo_box_load_selected_finish ( combo_box, result, &contents, &length, &is_html, &error); @@ -690,10 +690,10 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box, } editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); - new_signature_id = e_html_editor_view_insert_signature ( - view, + new_signature_id = e_content_editor_insert_signature ( + cnt_editor, contents, is_html, gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box)), @@ -710,13 +710,11 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box, } static void -html_editor_view_is_ready_cb (EHTMLEditorView *view, - EMsgComposer *composer) +content_editor_load_finished_cb (EContentEditor *cnt_editor, + EMsgComposer *composer) { g_signal_handlers_disconnect_by_func ( - view, - G_CALLBACK (html_editor_view_is_ready_cb), - composer); + cnt_editor, G_CALLBACK (content_editor_load_finished_cb), composer); e_composer_update_signature (composer); } @@ -727,7 +725,7 @@ e_composer_update_signature (EMsgComposer *composer) EComposerHeaderTable *table; EMailSignatureComboBox *combo_box; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); @@ -739,13 +737,13 @@ e_composer_update_signature (EMsgComposer *composer) table = e_msg_composer_get_header_table (composer); combo_box = e_composer_header_table_get_signature_combo_box (table); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); - if (!e_html_editor_view_is_ready (view)) { + if (!e_content_editor_is_ready (cnt_editor)) { g_signal_connect ( - view, "is-ready", - G_CALLBACK (html_editor_view_is_ready_cb), composer); - + cnt_editor, "load-finished", + G_CALLBACK (content_editor_load_finished_cb), + composer); return; } diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h index 9a90499..b2c50df 100644 --- a/composer/e-composer-private.h +++ b/composer/e-composer-private.h @@ -95,17 +95,17 @@ struct _EMsgComposerPrivate { gboolean busy; gboolean disable_signature; gboolean is_from_draft; + gboolean is_from_new_message; /* The web view is uneditable while the editor is busy. * This is used to restore the previous editable state. */ gboolean saved_editable; gboolean set_signature_from_message; gboolean drop_occured; gboolean dnd_is_uri; - gboolean check_if_signature_is_changed; - gboolean ignore_next_signature_change; gboolean is_sending_message; gboolean dnd_history_saved; - gboolean ignore_next_paste_clipboard_signals_emission; + gboolean check_if_signature_is_changed; + gboolean ignore_next_signature_change; gboolean last_signal_was_paste_primary; gint focused_entry_selection_start; diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c index c472fcc..713d02a 100644 --- a/composer/e-msg-composer.c +++ b/composer/e-msg-composer.c @@ -133,7 +133,7 @@ static void add_attachments_from_multipart (EMsgComposer *composer, gboolean just_inlines, gint depth); -/* used by e_msg_composer_new_with_message () */ +/* used by e_msg_composer_setup_with_message () */ static void handle_multipart (EMsgComposer *composer, CamelMultipart *multipart, gboolean keep_signature, @@ -1043,11 +1043,11 @@ composer_add_evolution_composer_mode_header (CamelMedium *medium, { gboolean html_mode; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - html_mode = e_html_editor_view_get_html_mode (view); + cnt_editor = e_html_editor_get_content_editor (editor); + html_mode = e_content_editor_get_html_mode (cnt_editor); camel_medium_add_header ( medium, @@ -1262,12 +1262,18 @@ composer_build_message (EMsgComposer *composer, } else { gchar *text; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); data = g_byte_array_new (); - text = e_html_editor_view_get_text_plain (view); + + text = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_PLAIN | + E_CONTENT_EDITOR_GET_PROCESSED, + NULL, NULL); + g_byte_array_append (data, (guint8 *) text, strlen (text)); g_free (text); @@ -1330,15 +1336,14 @@ composer_build_message (EMsgComposer *composer, if ((flags & COMPOSER_FLAG_HTML_CONTENT) != 0 || (flags & COMPOSER_FLAG_SAVE_DRAFT) != 0) { gchar *text; - guint count; gsize length; gboolean pre_encode; EHTMLEditor *editor; - EHTMLEditorView *view; - GList *inline_images = NULL; + EContentEditor *cnt_editor; + GSList *inline_images_parts = NULL, *link; editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); data = g_byte_array_new (); if ((flags & COMPOSER_FLAG_SAVE_DRAFT) != 0) { @@ -1350,10 +1355,19 @@ composer_build_message (EMsgComposer *composer, composer_add_evolution_composer_mode_header ( CAMEL_MEDIUM (context->message), composer); - text = e_html_editor_view_get_text_html_for_drafts_with_images ( - view, from_domain, &inline_images); - } else - text = e_html_editor_view_get_text_html (view, from_domain, &inline_images); + text = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_HTML | + E_CONTENT_EDITOR_GET_INLINE_IMAGES, + from_domain, &inline_images_parts); + } else { + text = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_HTML | + E_CONTENT_EDITOR_GET_PROCESSED | + E_CONTENT_EDITOR_GET_INLINE_IMAGES, + from_domain, &inline_images_parts); + } length = strlen (text); g_byte_array_append (data, (guint8 *) text, (guint) length); @@ -1409,9 +1423,7 @@ composer_build_message (EMsgComposer *composer, /* If there are inlined images, construct a multipart/related * containing the multipart/alternative and the images. */ - count = g_list_length (inline_images); - if (count > 0) { - guint ii; + if (inline_images_parts) { CamelMultipart *html_with_images; html_with_images = camel_multipart_new (); @@ -1430,10 +1442,10 @@ composer_build_message (EMsgComposer *composer, g_object_unref (body); - for (ii = 0; ii < count; ii++) { - CamelMimePart *part = g_list_nth_data (inline_images, ii); - camel_multipart_add_part ( - html_with_images, part); + for (link = inline_images_parts; link; link = g_slist_next (link)) { + CamelMimePart *part = link->data; + + camel_multipart_add_part (html_with_images, part); } context->top_level_part = @@ -1442,7 +1454,7 @@ composer_build_message (EMsgComposer *composer, context->top_level_part = CAMEL_DATA_WRAPPER (body); } - g_list_free_full (inline_images, g_object_unref); + g_slist_free_full (inline_images_parts, g_object_unref); } /* If there are attachments, wrap what we've built so far @@ -1553,18 +1565,26 @@ set_editor_text (EMsgComposer *composer, gboolean set_signature) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); g_return_if_fail (text != NULL); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); if (is_html) - e_html_editor_view_set_text_html (view, text); + e_content_editor_insert_content ( + cnt_editor, + text, + E_CONTENT_EDITOR_INSERT_TEXT_HTML | + E_CONTENT_EDITOR_INSERT_REPLACE_ALL); else - e_html_editor_view_set_text_plain (view, text); + e_content_editor_insert_content ( + cnt_editor, + text, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN | + E_CONTENT_EDITOR_INSERT_REPLACE_ALL); if (set_signature) e_composer_update_signature (composer); @@ -1581,10 +1601,10 @@ attachment_store_changed_cb (EMsgComposer *composer) * changes on close. */ editor = e_msg_composer_get_editor (composer); if (editor) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_changed (cnt_editor, TRUE); } } @@ -1694,8 +1714,8 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard, gint n_targets, EMsgComposer *composer) { - EHTMLEditor *editor = e_msg_composer_get_editor (composer); - EHTMLEditorView *editor_view = e_html_editor_get_view (editor); + EHTMLEditor *editor; + EContentEditor *cnt_editor; if (targets == NULL || n_targets < 0) return; @@ -1708,62 +1728,55 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard, return; } - if (!e_html_editor_view_get_html_mode (editor_view) && + editor = e_msg_composer_get_editor (composer); + cnt_editor = e_html_editor_get_content_editor (editor); + + if (!e_content_editor_get_html_mode (cnt_editor) && gtk_targets_include_image (targets, n_targets, TRUE)) { e_composer_paste_image (composer, clipboard); return; } - composer->priv->ignore_next_paste_clipboard_signals_emission = TRUE; - - g_signal_emit_by_name ( - editor_view, - composer->priv->last_signal_was_paste_primary ? - "paste-primary-clipboard" : "paste-clipboard"); + if (composer->priv->last_signal_was_paste_primary) { + e_content_editor_paste_primary (cnt_editor); + } else + e_content_editor_paste (cnt_editor); } -static void -msg_composer_paste_primary_clipboard_cb (EHTMLEditorView *view, +static gboolean +msg_composer_paste_primary_clipboard_cb (EContentEditor *cnt_editor, EMsgComposer *composer) { - if (composer->priv->ignore_next_paste_clipboard_signals_emission) - composer->priv->ignore_next_paste_clipboard_signals_emission = FALSE; - else { - GtkClipboard *clipboard; + GtkClipboard *clipboard; - clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); + clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); - composer->priv->last_signal_was_paste_primary = TRUE; + composer->priv->last_signal_was_paste_primary = TRUE; - gtk_clipboard_request_targets ( - clipboard, (GtkClipboardTargetsReceivedFunc) - msg_composer_paste_clipboard_targets_cb, composer); + gtk_clipboard_request_targets ( + clipboard, (GtkClipboardTargetsReceivedFunc) + msg_composer_paste_clipboard_targets_cb, composer); - g_signal_stop_emission_by_name (view, "paste-primary-clipboard"); - } + return TRUE; } -static void -msg_composer_paste_clipboard_cb (EHTMLEditorView *view, +static gboolean +msg_composer_paste_clipboard_cb (EContentEditor *cnt_editor, EMsgComposer *composer) { - if (composer->priv->ignore_next_paste_clipboard_signals_emission) - composer->priv->ignore_next_paste_clipboard_signals_emission = FALSE; - else { - GtkClipboard *clipboard; + GtkClipboard *clipboard; - clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); - composer->priv->last_signal_was_paste_primary = FALSE; + composer->priv->last_signal_was_paste_primary = FALSE; - gtk_clipboard_request_targets ( - clipboard, (GtkClipboardTargetsReceivedFunc) - msg_composer_paste_clipboard_targets_cb, composer); + gtk_clipboard_request_targets ( + clipboard, (GtkClipboardTargetsReceivedFunc) + msg_composer_paste_clipboard_targets_cb, composer); - g_signal_stop_emission_by_name (view, "paste-clipboard"); - } + return TRUE; } - +#if 0 /* FIXME WK2 */ static gboolean msg_composer_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, @@ -1792,50 +1805,6 @@ msg_composer_drag_motion_cb (GtkWidget *widget, return FALSE; } -static void -insert_nbsp_history_event (EHTMLEditorView *editor_view, - gboolean delete, - guint x, - guint y) -{ - EHTMLEditorViewHistoryEvent *event; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (editor_view)); - - event = g_new0 (EHTMLEditorViewHistoryEvent, 1); - event->type = HISTORY_AND; - e_html_editor_view_insert_new_history_event (editor_view, event); - - fragment = webkit_dom_document_create_document_fragment (document); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - webkit_dom_document_create_text_node (document, UNICODE_NBSP)), - NULL); - - event = g_new0 (EHTMLEditorViewHistoryEvent, 1); - event->type = HISTORY_DELETE; - - if (delete) - g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (1)); - - event->data.fragment = fragment; - - event->before.start.x = x; - event->before.start.y = y; - event->before.end.x = x; - event->before.end.y = y; - - event->after.start.x = x; - event->after.start.y = y; - event->after.end.x = x; - event->after.end.y = y; - - e_html_editor_view_insert_new_history_event (editor_view, event); -} - static gboolean msg_composer_drag_drop_cb (GtkWidget *widget, GdkDragContext *context, @@ -1855,195 +1824,21 @@ msg_composer_drag_drop_cb (GtkWidget *widget, EHTMLEditorView *editor_view = e_html_editor_get_view (editor); if ((gpointer) editor_view == (gpointer) source_widget) { - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *event; - gboolean start_to_start, end_to_end; - gchar *range_text; - guint x, y; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMDOMWindow *dom_window; - WebKitDOMRange *beginning_of_line = NULL; - WebKitDOMRange *range = NULL, *range_clone = NULL; - - selection = e_html_editor_view_get_selection (editor_view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (editor_view)); - - if (!(dom_window = webkit_dom_document_get_default_view (document))) - return FALSE; - - if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) { - g_object_unref (dom_window); - return FALSE; - } - - if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { - g_object_unref (dom_selection); - g_object_unref (dom_window); - return FALSE; - } - - /* Obtain the dragged content. */ - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - range_clone = webkit_dom_range_clone_range (range, NULL); - - /* Create the history event for the content that will - * be removed by DnD. */ - event = g_new0 (EHTMLEditorViewHistoryEvent, 1); - event->type = HISTORY_DELETE; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &event->before.start.x, - &event->before.start.y, - &event->before.end.x, - &event->before.end.y); - - x = event->before.start.x; - y = event->before.start.y; - - event->after.start.x = x; - event->after.start.y = y; - event->after.end.x = x; - event->after.end.y = y; - - /* Save the content that will be removed. */ - fragment = webkit_dom_range_clone_contents (range_clone, NULL); - - /* Extend the cloned range to point one character after - * the selection ends to later check if there is a whitespace - * after it. */ - webkit_dom_range_set_end ( - range_clone, - webkit_dom_range_get_end_container (range_clone, NULL), - webkit_dom_range_get_end_offset (range_clone, NULL) + 1, - NULL); - range_text = webkit_dom_range_get_text (range_clone); - - /* Check if the current selection starts on the beginning - * of line. */ - webkit_dom_dom_selection_modify ( - dom_selection, "extend", "left", "lineboundary"); - beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - start_to_start = webkit_dom_range_compare_boundary_points ( - beginning_of_line, 0 /* START_TO_START */, range, NULL) == 0; - - /* Restore the selection to state before the check. */ - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (beginning_of_line); - - /* Check if the current selection end on the end of the line. */ - webkit_dom_dom_selection_modify ( - dom_selection, "extend", "right", "lineboundary"); - beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - end_to_end = webkit_dom_range_compare_boundary_points ( - beginning_of_line, 2 /* END_TO_END */, range, NULL) == 0; - - /* Dragging the whole line. */ - if (start_to_start && end_to_end) { - WebKitDOMNode *container, *actual_block, *tmp_block; - - /* Select the whole line (to the beginning of the next - * one so we can reuse the undo code while undoing this. - * Because of this we need to special mark the event - * with history-drag-and-drop to correct the selection - * after undoing it (otherwise the beginning of the next - * line will be selected as well. */ - webkit_dom_dom_selection_modify ( - dom_selection, "extend", "right", "character"); - g_object_unref (beginning_of_line); - beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - - container = webkit_dom_range_get_end_container (range, NULL); - actual_block = e_html_editor_get_parent_block_node_from_child (container); - - tmp_block = webkit_dom_range_get_end_container (beginning_of_line, NULL); - if ((tmp_block = e_html_editor_get_parent_block_node_from_child (tmp_block))) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &event->before.start.x, - &event->before.start.y, - &event->before.end.x, - &event->before.end.y); - - /* Create the right content for the history event. */ - fragment = webkit_dom_document_create_document_fragment (document); - /* The removed line. */ - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (actual_block, TRUE), - NULL); - /* The following block, but empty. */ - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (tmp_block, FALSE), - NULL); - g_object_set_data ( - G_OBJECT (fragment), - "history-drag-and-drop", - GINT_TO_POINTER (1)); - /* It should act as a Delete key press. */ - g_object_set_data ( - G_OBJECT (fragment), - "history-delete-key", - GINT_TO_POINTER (1)); - } - } - - event->data.fragment = fragment; - e_html_editor_view_insert_new_history_event (editor_view, event); - - /* Selection is ending on the end of the line, check if - * there is a space before the selection start. If so, it - * will be removed and we need create the history event - * for it. */ - if (end_to_end) { - gchar *range_text_start; - glong start_offset; - - start_offset = webkit_dom_range_get_start_offset (range_clone, NULL); - webkit_dom_range_set_start ( - range_clone, - webkit_dom_range_get_start_container (range_clone, NULL), - start_offset > 0 ? start_offset - 1 : 0, + GDBusProxy *web_extension; + + web_extension = e_html_editor_view_get_web_extension_proxy (editor_view); + if (web_extension) { + g_dbus_proxy_call_sync ( + web_extension, + "DOMSaveDragAndDropHistory", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (editor_view))), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, NULL); - - range_text_start = webkit_dom_range_get_text (range_clone); - if (g_str_has_prefix (range_text_start, " ") || - g_str_has_prefix (range_text_start, UNICODE_NBSP)) - insert_nbsp_history_event (editor_view, FALSE, x, y); - - g_free (range_text_start); } - - /* WebKit removes the space (if presented) after selection and - * we need to create a new history event for it. */ - if (g_str_has_suffix (range_text, " ") || - g_str_has_suffix (range_text, UNICODE_NBSP)) - insert_nbsp_history_event (editor_view, TRUE, x, y); - - g_free (range_text); - - /* Restore the selection to original state. */ - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (beginning_of_line); - - /* All the things above were about removing the content, - * create an AND event to continue later with inserting - * the dropped content. */ - event = g_new0 (EHTMLEditorViewHistoryEvent, 1); - event->type = HISTORY_AND; - e_html_editor_view_insert_new_history_event (editor_view, event); - - g_object_unref (dom_selection); - g_object_unref (dom_window); - - g_object_unref (range); - g_object_unref (range_clone); - return FALSE; } } @@ -2086,6 +1881,7 @@ msg_composer_drag_data_received_after_cb (GtkWidget *widget, { EHTMLEditor *editor; EHTMLEditorView *view; + GDBusProxy *web_extension; if (!composer->priv->drop_occured) goto out; @@ -2096,8 +1892,20 @@ msg_composer_drag_data_received_after_cb (GtkWidget *widget, editor = e_msg_composer_get_editor (composer); view = e_html_editor_get_view (editor); - e_html_editor_view_save_history_for_drop (view); - e_html_editor_view_check_magic_links (view, FALSE); + web_extension = e_html_editor_view_get_web_extension_proxy (view); + if (!web_extension) + goto out; + + g_dbus_proxy_call_sync ( + web_extension, + "DOMCleanAfterDragAndDrop", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view))), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); out: composer->priv->drop_occured = FALSE; @@ -2140,28 +1948,22 @@ msg_composer_drag_data_received_cb (GtkWidget *widget, EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *html_editor_view; - EHTMLEditorSelection *editor_selection; + EContentEditor *cnt_editor; gboolean html_mode, same_widget = FALSE; GtkWidget *source_widget; editor = e_msg_composer_get_editor (composer); - html_editor_view = e_html_editor_get_view (editor); - html_mode = e_html_editor_view_get_html_mode (html_editor_view); - editor_selection = e_html_editor_view_get_selection (html_editor_view); + cnt_editor = e_html_editor_get_content_editor (editor); + html_mode = e_content_editor_get_html_mode (cnt_editor); composer->priv->dnd_history_saved = TRUE; /* When we are doing DnD just inside the web view, the DnD is supposed * to move things around. */ source_widget = gtk_drag_get_source_widget (context); - if (E_IS_HTML_EDITOR_VIEW (source_widget)) { - EHTMLEditor *editor = e_msg_composer_get_editor (composer); - EHTMLEditorView *editor_view = e_html_editor_get_view (editor); - - if ((gpointer) editor_view == (gpointer) source_widget) - same_widget = TRUE; - } + if (E_IS_CONTENT_EDITOR (source_widget) && + ((gpointer) cnt_editor == (gpointer) source_widget)) + same_widget = TRUE; /* Leave DnD inside the view on WebKit. */ if (composer->priv->drop_occured && same_widget) { @@ -2215,21 +2017,20 @@ msg_composer_drag_data_received_cb (GtkWidget *widget, return; } - e_html_editor_selection_set_on_point (editor_selection, x, y); + e_content_editor_move_caret_on_coordinates (cnt_editor, x, y, FALSE); list_len = length; do { text = next_uri ((guchar **) &data, &len, &list_len); - e_html_editor_selection_insert_html (editor_selection, text); + e_content_editor_insert_content ( + cnt_editor, + text, + E_CONTENT_EDITOR_INSERT_TEXT_HTML); g_free (text); } while (list_len); - e_html_editor_view_check_magic_links (html_editor_view, FALSE); - e_html_editor_view_force_spell_check_in_viewport (html_editor_view); - - e_html_editor_selection_scroll_to_caret (editor_selection); - gtk_drag_finish (context, TRUE, FALSE, time); + return; } @@ -2253,38 +2054,38 @@ msg_composer_drag_data_received_cb (GtkWidget *widget, return; } - e_html_editor_selection_set_on_point (editor_selection, x, y); + e_content_editor_move_caret_on_coordinates (cnt_editor, x, y, FALSE); list_len = length; do { uri = next_uri ((guchar **) &data, &len, &list_len); - e_html_editor_selection_insert_image (editor_selection, uri); + e_content_editor_insert_image (cnt_editor, uri); g_free (uri); } while (list_len); gtk_drag_finish (context, TRUE, FALSE, time); } else { - EAttachmentView *view = e_msg_composer_get_attachment_view (composer); - + EAttachmentView *attachment_view = + e_msg_composer_get_attachment_view (composer); /* Forward the data to the attachment view. Note that calling * e_attachment_view_drag_data_received() will not work because * that function only handles the case where all the other drag * handlers have failed. */ e_attachment_paned_drag_data_received ( - E_ATTACHMENT_PANED (view), + E_ATTACHMENT_PANED (attachment_view), context, x, y, selection, info, time); } } - +#endif static void msg_composer_notify_header_cb (EMsgComposer *composer) { + EContentEditor *cnt_editor; EHTMLEditor *editor; - EHTMLEditorView *view; editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_changed (cnt_editor, TRUE); } static gboolean @@ -2408,6 +2209,16 @@ msg_composer_quit_requested_cb (EShell *shell, } static void +msg_composer_set_editor (EMsgComposer *composer, + EHTMLEditor *editor) +{ + g_return_if_fail (E_IS_HTML_EDITOR (editor)); + g_return_if_fail (composer->priv->editor == NULL); + + composer->priv->editor = g_object_ref_sink (editor); +} + +static void msg_composer_set_shell (EMsgComposer *composer, EShell *shell) { @@ -2427,6 +2238,12 @@ msg_composer_set_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_EDITOR: + msg_composer_set_editor ( + E_MSG_COMPOSER (object), + g_value_get_object (value)); + return; + case PROP_SHELL: msg_composer_set_shell ( E_MSG_COMPOSER (object), @@ -2450,6 +2267,12 @@ msg_composer_get_property (GObject *object, E_MSG_COMPOSER (object))); return; + case PROP_EDITOR: + g_value_set_object ( + value, e_msg_composer_get_editor ( + E_MSG_COMPOSER (object))); + return; + case PROP_FOCUS_TRACKER: g_value_set_object ( value, e_msg_composer_get_focus_tracker ( @@ -2514,9 +2337,8 @@ composer_notify_activity_cb (EActivityBar *activity_bar, EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; - WebKitWebView *web_view; - gboolean editable; + EContentEditor *cnt_editor; + gboolean editable = TRUE; gboolean busy; busy = (e_activity_bar_get_activity (activity_bar) != NULL); @@ -2530,16 +2352,15 @@ composer_notify_activity_cb (EActivityBar *activity_bar, e_msg_composer_save_focused_widget (composer); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - web_view = WEBKIT_WEB_VIEW (view); + cnt_editor = e_html_editor_get_content_editor (editor); if (busy) { - editable = webkit_web_view_get_editable (web_view); - webkit_web_view_set_editable (web_view, FALSE); + editable = e_content_editor_is_editable (cnt_editor); + e_content_editor_set_editable (cnt_editor, FALSE); composer->priv->saved_editable = editable; } else { editable = composer->priv->saved_editable; - webkit_web_view_set_editable (web_view, editable); + e_content_editor_set_editable (cnt_editor, editable); } g_object_notify (G_OBJECT (composer), "busy"); @@ -2554,11 +2375,11 @@ msg_composer_constructed (GObject *object) EShell *shell; EMsgComposer *composer; EActivityBar *activity_bar; - EAttachmentView *view; + EAttachmentView *attachment_view; EAttachmentStore *store; EComposerHeaderTable *table; EHTMLEditor *editor; - EHTMLEditorView *html_editor_view; + EContentEditor *cnt_editor; GtkUIManager *ui_manager; GtkToggleAction *action; GtkTargetList *target_list; @@ -2568,16 +2389,21 @@ msg_composer_constructed (GObject *object) const gchar *id; gboolean active; + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_msg_composer_parent_class)->constructed (object); + composer = E_MSG_COMPOSER (object); + g_return_if_fail (E_IS_HTML_EDITOR (composer->priv->editor)); + shell = e_msg_composer_get_shell (composer); e_composer_private_constructed (composer); editor = e_msg_composer_get_editor (composer); - html_editor_view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); ui_manager = e_html_editor_get_ui_manager (editor); - view = e_msg_composer_get_attachment_view (composer); + attachment_view = e_msg_composer_get_attachment_view (composer); table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table); gtk_window_set_title (GTK_WINDOW (composer), _("Compose Message")); @@ -2625,40 +2451,45 @@ msg_composer_constructed (GObject *object) gtk_toggle_action_set_active (action, active); action = GTK_TOGGLE_ACTION (ACTION (UNICODE_SMILEYS)); - active = g_settings_get_boolean (settings, "composer-unicode-smileys"); - gtk_toggle_action_set_active (action, active); + g_settings_bind (settings, "composer-unicode-smileys", + action, "active", + G_SETTINGS_BIND_DEFAULT); + g_object_unref (settings); /* Clipboard Support */ g_signal_connect ( - html_editor_view, "paste-clipboard", + cnt_editor, "paste-clipboard", G_CALLBACK (msg_composer_paste_clipboard_cb), composer); g_signal_connect ( - html_editor_view, "paste-primary-clipboard", + cnt_editor, "paste-primary-clipboard", G_CALLBACK (msg_composer_paste_primary_clipboard_cb), composer); - e_html_editor_view_reconnect_paste_clipboard_signals (html_editor_view); /* Drag-and-Drop Support */ +#if 0 /* FIXME WK2 */ + EHTMLEditorView *view; + + view = e_html_editor_get_view (editor); g_signal_connect ( - html_editor_view, "drag-motion", + view, "drag-motion", G_CALLBACK (msg_composer_drag_motion_cb), composer); - g_signal_connect ( - html_editor_view, "drag-drop", + g_signal_connect ( + view, "drag-drop", G_CALLBACK (msg_composer_drag_drop_cb), composer); g_signal_connect ( - html_editor_view, "drag-data-received", + view, "drag-data-received", G_CALLBACK (msg_composer_drag_data_received_cb), composer); /* Used for fixing various stuff after WebKit processed the DnD data. */ g_signal_connect_after ( - html_editor_view, "drag-data-received", + view, "drag-data-received", G_CALLBACK (msg_composer_drag_data_received_after_cb), composer); - +#endif g_signal_connect ( composer->priv->gallery_icon_view, "drag-data-get", G_CALLBACK (msg_composer_gallery_drag_data_get), NULL); @@ -2694,7 +2525,7 @@ msg_composer_constructed (GObject *object) /* Attachments */ - store = e_attachment_view_get_store (view); + store = e_attachment_view_get_store (attachment_view); g_signal_connect_swapped ( store, "row-deleted", @@ -2705,12 +2536,12 @@ msg_composer_constructed (GObject *object) G_CALLBACK (attachment_store_changed_cb), composer); /* Initialization may have tripped the "changed" state. */ - e_html_editor_view_set_changed (html_editor_view, FALSE); + e_content_editor_set_changed (cnt_editor, FALSE); - target_list = e_attachment_view_get_target_list (view); + target_list = e_attachment_view_get_target_list (attachment_view); targets = gtk_target_table_new_from_list (target_list, &n_targets); - target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (html_editor_view)); + target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (cnt_editor)); gtk_target_list_add_table (target_list, drag_dest_targets, G_N_ELEMENTS (drag_dest_targets)); gtk_target_list_add_table (target_list, targets, n_targets); @@ -2724,8 +2555,6 @@ msg_composer_constructed (GObject *object) e_extensible_load_extensions (E_EXTENSIBLE (composer)); e_msg_composer_set_body_text (composer, "", TRUE); - /* Chain up to parent's constructed() method. */ - G_OBJECT_CLASS (e_msg_composer_parent_class)->constructed (object); } static void @@ -2782,7 +2611,7 @@ msg_composer_map (GtkWidget *widget) EComposerHeaderTable *table; GtkWidget *input_widget; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; const gchar *text; /* Chain up to parent's map() method. */ @@ -2813,8 +2642,8 @@ msg_composer_map (GtkWidget *widget) } /* Jump to the editor as a last resort. */ - view = e_html_editor_get_view (editor); - gtk_widget_grab_focus (GTK_WIDGET (view)); + cnt_editor = e_html_editor_get_content_editor (editor); + gtk_widget_grab_focus (GTK_WIDGET (cnt_editor)); } static gboolean @@ -2824,11 +2653,11 @@ msg_composer_key_press_event (GtkWidget *widget, EMsgComposer *composer; GtkWidget *input_widget; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; composer = E_MSG_COMPOSER (widget); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); input_widget = e_composer_header_table_get_header ( @@ -2848,15 +2677,15 @@ msg_composer_key_press_event (GtkWidget *widget, } if (event->keyval == GDK_KEY_Tab && gtk_widget_is_focus (input_widget)) { - gtk_widget_grab_focus (GTK_WIDGET (view)); + gtk_widget_grab_focus (GTK_WIDGET (cnt_editor)); return TRUE; } - if (gtk_widget_is_focus (GTK_WIDGET (view))) { + if (gtk_widget_is_focus (GTK_WIDGET (cnt_editor))) { if (event->keyval == GDK_KEY_ISO_Left_Tab) { gboolean view_processed = FALSE; - g_signal_emit_by_name (view, "key-press-event", event, &view_processed); + g_signal_emit_by_name (cnt_editor, "key-press-event", event, &view_processed); if (!view_processed) gtk_widget_grab_focus (input_widget); @@ -2942,6 +2771,17 @@ e_msg_composer_class_init (EMsgComposerClass *class) g_object_class_install_property ( object_class, + PROP_EDITOR, + g_param_spec_object ( + "editor", + NULL, + NULL, + E_TYPE_HTML_EDITOR, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property ( + object_class, PROP_FOCUS_TRACKER, g_param_spec_object ( "focus-tracker", @@ -3019,31 +2859,87 @@ e_msg_composer_class_init (EMsgComposerClass *class) static void e_msg_composer_init (EMsgComposer *composer) { - EHTMLEditorView *view; - composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer); +} - composer->priv->editor = g_object_ref_sink (e_html_editor_new ()); - view = e_html_editor_get_view (composer->priv->editor); - e_html_editor_view_set_is_editting_message (view, TRUE); +static void +e_msg_composer_editor_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GtkWidget *editor; + ESimpleAsyncResult *eresult = user_data; + GError *error = NULL; + + g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (eresult)); + + editor = e_html_editor_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + e_simple_async_result_set_op_pointer (eresult, editor); + e_simple_async_result_complete (eresult); + } + + g_object_unref (eresult); } /** * e_msg_composer_new: * @shell: an #EShell + * @callback: called when the composer is ready + * @user_data: user data passed to @callback + * + * Asynchronously creates an #EMsgComposer. The operation is finished + * with e_msg_composer_new_finish() called from within the @callback. + * + * Since: 3.22 + **/ +void +e_msg_composer_new (EShell *shell, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ESimpleAsyncResult *eresult; + + g_return_if_fail (E_IS_SHELL (shell)); + g_return_if_fail (callback != NULL); + + eresult = e_simple_async_result_new (NULL, callback, user_data, e_msg_composer_new); + e_simple_async_result_set_user_data (eresult, g_object_ref (shell), g_object_unref); + + e_html_editor_new (e_msg_composer_editor_created_cb, eresult); +} + +/** + * e_msg_composer_new_finish: + * @result: a #GAsyncResult provided by the callback from e_msg_composer_new() + * @error: optional #GError for errors * - * Create a new message composer widget. + * Finishes call of e_msg_composer_new(). * - * Returns: A pointer to the newly created widget + * Since: 3.22 **/ EMsgComposer * -e_msg_composer_new (EShell *shell) +e_msg_composer_new_finish (GAsyncResult *result, + GError **error) { - g_return_val_if_fail (E_IS_SHELL (shell), NULL); + ESimpleAsyncResult *eresult; + EHTMLEditor *html_editor; + + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, e_msg_composer_new), NULL); + + eresult = E_SIMPLE_ASYNC_RESULT (result); + + html_editor = e_simple_async_result_get_op_pointer (eresult); + g_return_val_if_fail (E_IS_HTML_EDITOR (html_editor), NULL); - return g_object_new ( - E_TYPE_MSG_COMPOSER, - "shell", shell, NULL); + return g_object_new (E_TYPE_MSG_COMPOSER, + "shell", e_simple_async_result_get_user_data (eresult), + "editor", html_editor, + NULL); } /** @@ -3110,7 +3006,7 @@ add_attachments_handle_mime_part (EMsgComposer *composer, CamelContentType *content_type; CamelDataWrapper *wrapper; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; if (!mime_part) return; @@ -3118,7 +3014,7 @@ add_attachments_handle_mime_part (EMsgComposer *composer, content_type = camel_mime_part_get_content_type (mime_part); wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); if (CAMEL_IS_MULTIPART (wrapper)) { /* another layer of multipartness... */ @@ -3128,10 +3024,10 @@ add_attachments_handle_mime_part (EMsgComposer *composer, } else if (just_inlines) { if (camel_mime_part_get_content_id (mime_part) || camel_mime_part_get_content_location (mime_part)) - e_html_editor_view_add_inline_image_from_mime_part ( - view, mime_part); + e_content_editor_insert_image_from_mime_part ( + cnt_editor, mime_part); } else if (related && camel_content_type_is (content_type, "image", "*")) { - e_html_editor_view_add_inline_image_from_mime_part (view, mime_part); + e_content_editor_insert_image_from_mime_part (cnt_editor, mime_part); } else if (camel_content_type_is (content_type, "text", "*") && camel_mime_part_get_filename (mime_part) == NULL) { /* Do nothing if this is a text/anything without a @@ -3525,56 +3421,26 @@ handle_multipart (EMsgComposer *composer, } } else if (depth == 0 && i == 0) { - EHTMLEditor *editor; - gboolean is_message_from_draft, is_html = FALSE; gchar *html = NULL; gssize length = 0; - editor = e_msg_composer_get_editor (composer); - is_message_from_draft = e_html_editor_view_is_message_from_draft ( - e_html_editor_get_view (editor)); - is_html = camel_content_type_is (content_type, "text", "html"); - /* Since the first part is not multipart/alternative, * this must be the body. */ + html = emcu_part_to_html ( + composer, mime_part, &length, keep_signature, cancellable); - /* If we are opening message from Drafts */ - if (is_message_from_draft) { - /* Extract the body */ - CamelDataWrapper *dw; - - dw = camel_medium_get_content ((CamelMedium *) mime_part); - if (dw) { - CamelStream *mem = camel_stream_mem_new (); - GByteArray *bytes; - - camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL); - camel_stream_close (mem, cancellable, NULL); - - bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem)); - if (bytes && bytes->len) - html = g_strndup ((const gchar *) bytes->data, bytes->len); - - g_object_unref (mem); - } - } else { - is_html = TRUE; - html = emcu_part_to_html ( - composer, mime_part, &length, keep_signature, cancellable); - } - - if (html) - e_msg_composer_set_pending_body (composer, html, length, is_html); + e_msg_composer_set_pending_body (composer, html, length, TRUE); } else if (camel_mime_part_get_content_id (mime_part) || camel_mime_part_get_content_location (mime_part)) { /* special in-line attachment */ EHTMLEditor *editor; + EContentEditor *cnt_editor; editor = e_msg_composer_get_editor (composer); - e_html_editor_view_add_inline_image_from_mime_part ( - e_html_editor_get_view (editor), mime_part); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_image_from_mime_part (cnt_editor, mime_part); } else { /* normal attachment */ e_msg_composer_attach (composer, mime_part); @@ -3586,9 +3452,7 @@ static void set_signature_gui (EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; - WebKitDOMDocument *document; - WebKitDOMElement *element; + EContentEditor *cnt_editor; EComposerHeaderTable *table; EMailSignatureComboBox *combo_box; gchar *uid = NULL; @@ -3597,16 +3461,11 @@ set_signature_gui (EMsgComposer *composer) combo_box = e_composer_header_table_get_signature_combo_box (table); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if ((element = webkit_dom_document_query_selector (document, ".-x-evo-signature[id]", NULL))) - uid = webkit_dom_element_get_id (element); + cnt_editor = e_html_editor_get_content_editor (editor); - /* The combo box active ID is the signature's ESource UID. */ - if (uid != NULL) { + if ((uid = e_content_editor_get_current_signature_uid (cnt_editor))) { + /* The combo box active ID is the signature's ESource UID. */ gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), uid); - g_free (uid); } } @@ -3657,25 +3516,25 @@ composer_add_auto_recipients (ESource *source, } /** - * e_msg_composer_new_with_message: - * @shell: an #EShell + * e_msg_composer_setup_with_message: + * @composer: an #EMsgComposer * @message: The message to use as the source * @keep_signature: Keep message signature, if any * @override_identity_uid: (allow none): Optional identity UID to use, or %NULL * @cancellable: optional #GCancellable object, or %NULL * - * Create a new message composer widget. + * Sets up the message @composer with a specific @message. * * Note: Designed to work only for messages constructed using Evolution. * - * Returns: A pointer to the newly created widget + * Since: 3.22 **/ -EMsgComposer * -e_msg_composer_new_with_message (EShell *shell, - CamelMimeMessage *message, - gboolean keep_signature, - const gchar *override_identity_uid, - GCancellable *cancellable) +void +e_msg_composer_setup_with_message (EMsgComposer *composer, + CamelMimeMessage *message, + gboolean keep_signature, + const gchar *override_identity_uid, + GCancellable *cancellable) { CamelInternetAddress *from, *to, *cc, *bcc; GList *To = NULL, *Cc = NULL, *Bcc = NULL, *postto = NULL; @@ -3685,19 +3544,18 @@ e_msg_composer_new_with_message (EShell *shell, CamelContentType *content_type; struct _camel_header_raw *headers; CamelDataWrapper *content; - EMsgComposer *composer; EMsgComposerPrivate *priv; EComposerHeaderTable *table; ESource *source = NULL; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GtkToggleAction *action; struct _camel_header_raw *xev; gchar *identity_uid; gint len, i; gboolean is_message_from_draft = FALSE; - g_return_val_if_fail (E_IS_SHELL (shell), NULL); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); headers = CAMEL_MIME_PART (message)->headers; while (headers != NULL) { @@ -3711,11 +3569,10 @@ e_msg_composer_new_with_message (EShell *shell, headers = headers->next; } - composer = e_msg_composer_new (shell); priv = E_MSG_COMPOSER_GET_PRIVATE (composer); - editor = e_msg_composer_get_editor (composer); table = e_msg_composer_get_header_table (composer); - view = e_html_editor_get_view (editor); + editor = e_msg_composer_get_editor (composer); + cnt_editor = e_html_editor_get_content_editor (editor); if (postto) { e_composer_header_table_set_post_to_list (table, postto); @@ -3737,7 +3594,7 @@ e_msg_composer_new_with_message (EShell *shell, } if (!identity_uid) { source = em_utils_guess_mail_identity_with_recipients ( - e_shell_get_registry (shell), message, NULL, NULL); + e_shell_get_registry (e_msg_composer_get_shell (composer)), message, NULL, NULL); if (source) identity_uid = e_source_dup_uid (source); } @@ -3878,10 +3735,8 @@ e_msg_composer_new_with_message (EShell *shell, composer_mode = camel_medium_get_header ( CAMEL_MEDIUM (message), "X-Evolution-Composer-Mode"); - if (composer_mode && *composer_mode) { + if (composer_mode && *composer_mode) is_message_from_draft = TRUE; - e_html_editor_view_set_is_message_from_draft (view, TRUE); - } if (format != NULL) { gchar **flags; @@ -3891,22 +3746,12 @@ e_msg_composer_new_with_message (EShell *shell, flags = g_strsplit (format, ", ", 0); for (i = 0; flags[i]; i++) { - if (g_ascii_strcasecmp (flags[i], "text/html") == 0) { - if (composer_mode && g_ascii_strcasecmp (composer_mode, "text/html") == 0) { - e_html_editor_view_set_html_mode ( - view, TRUE); - } else { - e_html_editor_view_set_html_mode ( - view, FALSE); - } - } else if (g_ascii_strcasecmp (flags[i], "text/plain") == 0) { - if (composer_mode && g_ascii_strcasecmp (composer_mode, "text/html") == 0) { - e_html_editor_view_set_html_mode ( - view, TRUE); - } else { - e_html_editor_view_set_html_mode ( - view, FALSE); - } + if (g_ascii_strcasecmp (flags[i], "text/html") == 0 || + g_ascii_strcasecmp (flags[i], "text/plain") == 0) { + gboolean html_mode; + + html_mode = composer_mode && !g_ascii_strcasecmp (composer_mode, "text/html"); + e_content_editor_set_html_mode (cnt_editor, html_mode); } else if (g_ascii_strcasecmp (flags[i], "pgp-sign") == 0) { action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); gtk_toggle_action_set_active (action, TRUE); @@ -4040,7 +3885,6 @@ e_msg_composer_new_with_message (EShell *shell, e_msg_composer_set_pending_body (composer, html, length, is_html); } - e_html_editor_view_set_is_message_from_edit_as_new (view, TRUE); priv->set_signature_from_message = TRUE; /* We wait until now to set the body text because we need to @@ -4049,38 +3893,36 @@ e_msg_composer_new_with_message (EShell *shell, e_msg_composer_flush_pending_body (composer); set_signature_gui (composer); - - return composer; } /** - * e_msg_composer_new_redirect: - * @shell: an #EShell + * e_msg_composer_setup_redirect: + * @composer: an #EMsgComposer * @message: The message to use as the source + * @identity_uid: (nullable): an identity UID to use, if any + * @cancellable: an optional #GCancellable * - * Create a new message composer widget. + * Sets up the message @composer as a redirect of the @message. * - * Returns: A pointer to the newly created widget + * Since: 3.22 **/ -EMsgComposer * -e_msg_composer_new_redirect (EShell *shell, - CamelMimeMessage *message, - const gchar *identity_uid, - GCancellable *cancellable) +void +e_msg_composer_setup_redirect (EMsgComposer *composer, + CamelMimeMessage *message, + const gchar *identity_uid, + GCancellable *cancellable) { - EMsgComposer *composer; EComposerHeaderTable *table; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; const gchar *subject; - g_return_val_if_fail (E_IS_SHELL (shell), NULL); - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); - composer = e_msg_composer_new_with_message ( - shell, message, TRUE, identity_uid, cancellable); - table = e_msg_composer_get_header_table (composer); + e_msg_composer_setup_with_message (composer, message, TRUE, identity_uid, cancellable); + table = e_msg_composer_get_header_table (composer); subject = camel_mime_message_get_subject (message); composer->priv->redirect = message; @@ -4089,10 +3931,8 @@ e_msg_composer_new_redirect (EShell *shell, e_composer_header_table_set_subject (table, subject); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE); - - return composer; + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_editable (cnt_editor, FALSE); } /** @@ -4149,7 +3989,7 @@ msg_composer_send_cb (EMsgComposer *composer, CamelMimeMessage *message; EAlertSink *alert_sink; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GError *error = NULL; alert_sink = e_activity_get_alert_sink (context->activity); @@ -4182,8 +4022,8 @@ msg_composer_send_cb (EMsgComposer *composer, /* The callback can set editor 'changed' if anything failed. */ editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_changed (cnt_editor, TRUE); composer->priv->is_sending_message = TRUE; @@ -4241,15 +4081,15 @@ msg_composer_save_to_drafts_done_cb (gpointer user_data, { EMsgComposer *composer = user_data; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); if (e_msg_composer_is_exiting (composer) && - !e_html_editor_view_get_changed (view)) { + !e_content_editor_get_changed (cnt_editor)) { gtk_widget_destroy (GTK_WIDGET (composer)); } else if (e_msg_composer_is_exiting (composer)) { gtk_widget_set_sensitive (GTK_WIDGET (composer), TRUE); @@ -4266,7 +4106,7 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer, CamelMimeMessage *message; EAlertSink *alert_sink; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GError *error = NULL; alert_sink = e_activity_get_alert_sink (context->activity); @@ -4308,8 +4148,8 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer, /* The callback can set editor 'changed' if anything failed. */ editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, FALSE); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_changed (cnt_editor, TRUE); g_signal_emit ( composer, signals[SAVE_TO_DRAFTS], @@ -4361,7 +4201,7 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer, CamelMimeMessage *message; EAlertSink *alert_sink; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GError *error = NULL; alert_sink = e_activity_get_alert_sink (context->activity); @@ -4397,8 +4237,8 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer, async_context_free (context); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, FALSE); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_changed (cnt_editor, TRUE); } /** @@ -4821,27 +4661,22 @@ handle_mailto (EMsgComposer *composer, } /** - * e_msg_composer_new_from_url: - * @shell: an #EShell + * e_msg_composer_setup_from_url: + * @composer: an #EMsgComposer * @url: a mailto URL * - * Create a new message composer widget, and fill in fields as - * defined by the provided URL. + * Sets up the message @composer content as defined by the provided URL. + * + * Since: 3.22 **/ -EMsgComposer * -e_msg_composer_new_from_url (EShell *shell, - const gchar *url) +void +e_msg_composer_setup_from_url (EMsgComposer *composer, + const gchar *url) { - EMsgComposer *composer; - - g_return_val_if_fail (E_IS_SHELL (shell), NULL); - g_return_val_if_fail (g_ascii_strncasecmp (url, "mailto:", 7) == 0, NULL); - - composer = e_msg_composer_new (shell); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (g_ascii_strncasecmp (url, "mailto:", 7) == 0); handle_mailto (composer, url); - - return composer; } /** @@ -4881,7 +4716,7 @@ e_msg_composer_set_body (EMsgComposer *composer, EMsgComposerPrivate *priv = composer->priv; EComposerHeaderTable *table; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; ESource *source; const gchar *identity_uid; const gchar *content; @@ -4889,7 +4724,7 @@ e_msg_composer_set_body (EMsgComposer *composer, g_return_if_fail (E_IS_MSG_COMPOSER (composer)); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); table = e_msg_composer_get_header_table (composer); /* Disable signature */ @@ -4901,8 +4736,8 @@ e_msg_composer_set_body (EMsgComposer *composer, content = _("The composer contains a non-text message body, which cannot be edited."); set_editor_text (composer, content, TRUE, FALSE); - e_html_editor_view_set_html_mode (view, FALSE); - webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE); + e_content_editor_set_html_mode (cnt_editor, FALSE); + e_content_editor_set_editable (cnt_editor, FALSE); g_free (priv->mime_body); priv->mime_body = g_strdup (body); @@ -5202,12 +5037,12 @@ e_msg_composer_get_message (EMsgComposer *composer, GtkAction *action; ComposerFlags flags = 0; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); simple = g_simple_async_result_new ( G_OBJECT (composer), callback, @@ -5215,7 +5050,7 @@ e_msg_composer_get_message (EMsgComposer *composer, g_simple_async_result_set_check_cancellable (simple, cancellable); - if (e_html_editor_view_get_html_mode (view)) + if (e_content_editor_get_html_mode (cnt_editor)) flags |= COMPOSER_FLAG_HTML_CONTENT; action = ACTION (PRIORITIZE_MESSAGE); @@ -5333,7 +5168,7 @@ e_msg_composer_get_message_draft (EMsgComposer *composer, gpointer user_data) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GSimpleAsyncResult *simple; ComposerFlags flags = COMPOSER_FLAG_SAVE_DRAFT; GtkAction *action; @@ -5347,9 +5182,9 @@ e_msg_composer_get_message_draft (EMsgComposer *composer, g_simple_async_result_set_check_cancellable (simple, cancellable); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); /* We need to remember composer mode */ - if (e_html_editor_view_get_html_mode (view)) + if (e_content_editor_get_html_mode (cnt_editor)) flags |= COMPOSER_FLAG_HTML_MODE; /* We want to save HTML content everytime when we save as draft */ flags |= COMPOSER_FLAG_SAVE_DRAFT; @@ -5482,44 +5317,22 @@ GByteArray * e_msg_composer_get_raw_message_text_without_signature (EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; - GByteArray *array; - gint ii, length; - WebKitDOMDocument *document; - WebKitDOMNodeList *list; + EContentEditor *cnt_editor; + gchar *content; g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - array = g_byte_array_new (); + cnt_editor = e_html_editor_get_content_editor (editor); - list = webkit_dom_document_query_selector_all ( - document, "body > *:not(.-x-evo-signature-wrapper)", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) { - gchar *text; - - text = webkit_dom_html_element_get_inner_text (WEBKIT_DOM_HTML_ELEMENT (node)); - g_byte_array_append (array, (guint8 *) text, strlen (text)); - g_free (text); + content = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_BODY | + E_CONTENT_EDITOR_GET_TEXT_PLAIN | + E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE, + NULL, NULL); - if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (node)) - g_byte_array_append (array, (const guint8 *) "\n", 1); - else - g_byte_array_append (array, (const guint8 *) " ", 1); - } - - g_object_unref (node); - } - - g_object_unref (list); - - return array; + return g_byte_array_new_take ((guint8 *) content, strlen (content)); } /** @@ -5531,25 +5344,21 @@ GByteArray * e_msg_composer_get_raw_message_text (EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; - GByteArray *array; - gchar *text; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; + EContentEditor *cnt_editor; + gchar *content; g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); + cnt_editor = e_html_editor_get_content_editor (editor); - array = g_byte_array_new (); - text = webkit_dom_html_element_get_inner_text (body); - g_byte_array_append (array, (guint8 *) text, strlen (text)); - g_free (text); + content = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_BODY | + E_CONTENT_EDITOR_GET_TEXT_PLAIN, + NULL, NULL); - return array; + return g_byte_array_new_take ((guint8 *) content, strlen (content)); } gboolean @@ -5580,7 +5389,7 @@ e_msg_composer_can_close (EMsgComposer *composer, { gboolean res = FALSE; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; EComposerHeaderTable *table; GdkWindow *window; GtkWidget *widget; @@ -5589,14 +5398,14 @@ e_msg_composer_can_close (EMsgComposer *composer, widget = GTK_WIDGET (composer); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); /* this means that there is an async operation running, * in which case the composer cannot be closed */ if (!gtk_action_group_get_sensitive (composer->priv->async_actions)) return FALSE; - if (!e_html_editor_view_get_changed (view)) + if (!e_content_editor_get_changed (cnt_editor)) return TRUE; window = gtk_widget_get_window (widget); @@ -5657,6 +5466,7 @@ e_save_spell_languages (const GList *spell_dicts) /* Build a list of spell check language codes. */ lang_array = g_ptr_array_new (); + while (spell_dicts != NULL) { ESpellDictionary *dict = spell_dicts->data; const gchar *language_code; @@ -5689,14 +5499,8 @@ e_msg_composer_save_focused_widget (EMsgComposer *composer) widget = gtk_window_get_focus (GTK_WINDOW (composer)); composer->priv->focused_entry = widget; - if (E_IS_HTML_EDITOR_VIEW (widget)) { - EHTMLEditorSelection *selection; - - selection = e_html_editor_view_get_selection ( - E_HTML_EDITOR_VIEW (widget)); - - e_html_editor_selection_save (selection); - } + if (E_IS_CONTENT_EDITOR (widget)) + e_content_editor_selection_save (E_CONTENT_EDITOR (widget)); if (GTK_IS_EDITABLE (widget)) { gtk_editable_get_selection_bounds ( @@ -5725,15 +5529,11 @@ e_msg_composer_restore_focus_on_composer (EMsgComposer *composer) composer->priv->focused_entry_selection_end); } - if (E_IS_HTML_EDITOR_VIEW (widget)) { - EHTMLEditorSelection *selection; - - e_html_editor_view_force_spell_check_in_viewport (E_HTML_EDITOR_VIEW (widget)); - - selection = e_html_editor_view_get_selection ( - E_HTML_EDITOR_VIEW (widget)); - - e_html_editor_selection_restore (selection); + if (E_IS_CONTENT_EDITOR (widget)) { + EContentEditor *cnt_editor = E_CONTENT_EDITOR (widget); + /* FIXME WK2 + e_html_editor_view_force_spell_check (view);*/ + e_content_editor_selection_restore (cnt_editor); } composer->priv->focused_entry = NULL; diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h index daaca2b..c0986a5 100644 --- a/composer/e-msg-composer.h +++ b/composer/e-msg-composer.h @@ -81,15 +81,20 @@ struct _EMsgComposerClass { }; GType e_msg_composer_get_type (void); -EMsgComposer * e_msg_composer_new (EShell *shell); -EMsgComposer * e_msg_composer_new_with_message (EShell *shell, +void e_msg_composer_new (EShell *shell, + GAsyncReadyCallback callback, + gpointer user_data); +EMsgComposer * e_msg_composer_new_finish (GAsyncResult *result, + GError **error); +void e_msg_composer_setup_with_message + (EMsgComposer *composer, CamelMimeMessage *message, gboolean keep_signature, const gchar *override_identity_uid, GCancellable *cancellable); -EMsgComposer * e_msg_composer_new_from_url (EShell *shell, +void e_msg_composer_setup_from_url (EMsgComposer *composer, const gchar *url); -EMsgComposer * e_msg_composer_new_redirect (EShell *shell, +void e_msg_composer_setup_redirect (EMsgComposer *composer, CamelMimeMessage *message, const gchar *identity_uid, GCancellable *cancellable); diff --git a/configure.ac b/configure.ac index ab0ab28..5439429 100644 --- a/configure.ac +++ b/configure.ac @@ -50,7 +50,7 @@ m4_define([gcr_minimum_version], [3.4]) m4_define([enchant_minimum_version], [1.1.7]) m4_define([gnome_desktop_minimum_version], [2.91.3]) m4_define([gsettings_desktop_schemas_minimum_version], [2.91.92]) -m4_define([webkitgtk_minimum_version], [2.2.0]) +m4_define([webkit2gtk_minimum_version], [2.13.0]) m4_define([libxml_minimum_version], [2.7.3]) m4_define([shared_mime_info_minimum_version], [0.22]) m4_define([libpst_minimum_version], [0.6.54]) @@ -287,7 +287,7 @@ PKG_CHECK_MODULES([GNOME_PLATFORM], libxml-2.0 >= libxml_minimum_version shared-mime-info >= shared_mime_info_minimum_version gsettings-desktop-schemas >= gsettings_desktop_schemas_minimum_version - webkitgtk-3.0 >= webkitgtk_minimum_version + webkit2gtk-4.0 >= webkitgtk_minimum_version $GIO_UNIX_REQUIREMENT]) GNOME_DESKTOP_DEPENDENCY="" @@ -1269,6 +1269,18 @@ AC_SUBST(viewsdir) dnl For evolution-alarm-notify.desktop AS_AC_EXPAND(PRIVLIBEXECDIR, "$privlibexecdir") +dnl ********************************** +dnl WebKit2 Web Extensions +dnl ********************************** +webextensionsdir="$privlibdir/web-extensions" +webextensionswebkiteditordir="$privlibdir/web-extensions/webkit-editor" +AC_SUBST(webextensionsdir) +AC_SUBST(webextensionswebkiteditordir) + +PKG_CHECK_MODULES(WEB_EXTENSION, [webkit2gtk-4.0 >= webkit2gtk_minimum_version]) +AC_SUBST(WEB_EXTENSIONS_CFLAGS) +AC_SUBST(WEB_EXTENSIONS_LIBS) + dnl ************************ dnl Plugins dnl ************************ @@ -1592,6 +1604,7 @@ modules/composer-autosave/Makefile modules/contact-photos/Makefile modules/gravatar/Makefile modules/itip-formatter/Makefile +modules/itip-formatter/web-extension/Makefile modules/mail-config/Makefile modules/mail/Makefile modules/mailto-handler/Makefile @@ -1607,7 +1620,9 @@ modules/startup-wizard/Makefile modules/text-highlight/Makefile modules/tnef-attachment/Makefile modules/vcard-inline/Makefile -modules/web-inspector/Makefile +modules/webkit-editor/Makefile +modules/webkit-editor/web-extension/Makefile +modules/webkit-inspector/Makefile plugins/Makefile plugins/attachment-reminder/Makefile plugins/bbdb/Makefile @@ -1622,6 +1637,7 @@ plugins/pst-import/Makefile plugins/publish-calendar/Makefile plugins/save-calendar/Makefile plugins/templates/Makefile +web-extensions/Makefile smime/Makefile smime/lib/Makefile smime/gui/Makefile diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in index 4c91054..3eca005 100644 --- a/data/org.gnome.evolution.mail.gschema.xml.in +++ b/data/org.gnome.evolution.mail.gschema.xml.in @@ -40,6 +40,11 @@ <_summary>Default charset in which to compose messages</_summary> <_description>Default charset in which to compose messages. Uses UTF-8, if not set.</_description> </key> + <key name="composer-editor" type="s"> + <default>''</default> + <_summary>Name of the editor to prefer in the message composer</_summary> + <_description>If the name doesn't correspond to any known editor, then the built-in WebKit editor is used.</_description> + </key> <key name="composer-gallery-path" type="s"> <default>''</default> <_summary>Path where picture gallery should search for its content</_summary> @@ -175,11 +180,6 @@ <_summary>List of localized 'Re'</_summary> <_description>Comma-separated list of localized 'Re' abbreviations to skip in a subject text when replying to a message, as an addition to the standard "Re" prefix. An example is 'SV,AV'.</_description> </key> - <key name="composer-developer-mode" type="b"> - <default>false</default> - <_summary>Enable developer mode</_summary> - <_description>Enables some hidden actions and tools aimed for development and debugging.</_description> - </key> <key name="composer-word-wrap-length" type="i"> <default>71</default> <_summary>Number of characters for wrapping</_summary> @@ -299,6 +299,11 @@ <_summary>Timeout for marking messages as seen</_summary> <_description>Timeout in milliseconds for marking messages as seen.</_description> </key> + <key name="show-attachment-bar" type="b"> + <default>true</default> + <_summary>Show Attachment Bar</_summary> + <_description>Show Attachment Bar below the message preview pane when the message has attachments.</_description> + </key> <key name="show-email" type="b"> <default>false</default> <_summary>Sender email-address column in the message list</_summary> diff --git a/doc/reference/evolution-util/Makefile.am b/doc/reference/evolution-util/Makefile.am index 2d1b169..db6074e 100644 --- a/doc/reference/evolution-util/Makefile.am +++ b/doc/reference/evolution-util/Makefile.am @@ -18,7 +18,6 @@ CFILE_GLOB = $(top_srcdir)/e-util/*.c IGNORE_HFILES = \ e-html-editor-actions.h \ e-html-editor-private.h \ - e-html-editor-utils.h \ e-marshal.h \ e-table-col-dnd.h \ e-table-defines.h \ diff --git a/e-util/Makefile.am b/e-util/Makefile.am index 46a6bea..4c9db7b 100644 --- a/e-util/Makefile.am +++ b/e-util/Makefile.am @@ -62,6 +62,7 @@ noinst_PROGRAMS = \ test-contact-store \ test-dateedit \ test-html-editor \ + test-html-editor-units \ test-mail-signatures \ test-name-selector \ test-preferences-window \ @@ -97,6 +98,7 @@ libevolution_util_la_CPPFLAGS = \ -DEVOLUTION_TOOLSDIR=\""$(privlibexecdir)"\" \ -DEVOLUTION_UIDIR=\""$(uidir)"\" \ -DEVOLUTION_RULEDIR=\"$(ruledir)\" \ + -DEVOLUTION_WEB_EXTENSIONS_DIR=\""$(webextensionsdir)"\" \ -DG_LOG_DOMAIN=\"evolution-util\" \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ @@ -119,7 +121,6 @@ evolution_util_include_HEADERS = \ e-alert-sink.h \ e-alert.h \ e-attachment-bar.h \ - e-attachment-button.h \ e-attachment-dialog.h \ e-attachment-handler-image.h \ e-attachment-handler.h \ @@ -173,6 +174,8 @@ evolution_util_include_HEADERS = \ e-config.h \ e-conflict-search-selector.h \ e-contact-store.h \ + e-content-editor.h \ + e-content-request.h \ e-data-capture.h \ e-dateedit.h \ e-datetime-format.h \ @@ -208,12 +211,9 @@ evolution_util_include_HEADERS = \ e-html-editor-page-dialog.h \ e-html-editor-paragraph-dialog.h \ e-html-editor-replace-dialog.h \ - e-html-editor-selection.h \ e-html-editor-spell-check-dialog.h \ e-html-editor-table-dialog.h \ e-html-editor-text-dialog.h \ - e-html-editor-utils.h \ - e-html-editor-view.h \ e-html-editor.h \ e-html-utils.h \ e-icon-factory.h \ @@ -272,6 +272,7 @@ evolution_util_include_HEADERS = \ e-selection-model.h \ e-selection.h \ e-send-options.h \ + e-simple-async-result.h \ e-sorter-array.h \ e-sorter.h \ e-source-combo-box.h \ @@ -394,7 +395,6 @@ libevolution_util_la_SOURCES = \ e-alert-sink.c \ e-alert.c \ e-attachment-bar.c \ - e-attachment-button.c \ e-attachment-dialog.c \ e-attachment-handler-image.c \ e-attachment-handler.c \ @@ -448,6 +448,8 @@ libevolution_util_la_SOURCES = \ e-config.c \ e-conflict-search-selector.c \ e-contact-store.c \ + e-content-editor.c \ + e-content-request.c \ e-data-capture.c \ e-dateedit.c \ e-datetime-format.c \ @@ -484,12 +486,9 @@ libevolution_util_la_SOURCES = \ e-html-editor-paragraph-dialog.c \ e-html-editor-private.h \ e-html-editor-replace-dialog.c \ - e-html-editor-selection.c \ e-html-editor-spell-check-dialog.c \ e-html-editor-table-dialog.c \ e-html-editor-text-dialog.c \ - e-html-editor-utils.c \ - e-html-editor-view.c \ e-html-editor.c \ e-html-utils.c \ e-icon-factory.c \ @@ -548,6 +547,7 @@ libevolution_util_la_SOURCES = \ e-selection-model.c \ e-selection.c \ e-send-options.c \ + e-simple-async-result.c \ e-sorter-array.c \ e-sorter.c \ e-source-combo-box.c \ @@ -694,6 +694,14 @@ test_html_editor_CPPFLAGS = $(TEST_CPPFLAGS) test_html_editor_SOURCES = test-html-editor.c test_html_editor_LDADD = $(TEST_LDADD) +test_html_editor_units_CPPFLAGS = $(TEST_CPPFLAGS) -DTEST_TOP_SRCDIR=\""$(top_srcdir)"\" +test_html_editor_units_SOURCES = \ + test-html-editor-units-utils.h \ + test-html-editor-units-utils.c \ + test-html-editor-units.c \ + $(NULL) +test_html_editor_units_LDADD = $(TEST_LDADD) + test_mail_signatures_CPPFLAGS = $(TEST_CPPFLAGS) test_mail_signatures_SOURCES = test-mail-signatures.c test_mail_signatures_LDADD = $(TEST_LDADD) diff --git a/e-util/e-attachment-bar.c b/e-util/e-attachment-bar.c index 3916d00..e5da99f 100644 --- a/e-util/e-attachment-bar.c +++ b/e-util/e-attachment-bar.c @@ -50,6 +50,8 @@ struct _EAttachmentBarPrivate { GtkWidget *status_label; GtkWidget *save_all_button; GtkWidget *save_one_button; + GtkWidget *icon_scrolled_window; /* not referenced */ + GtkWidget *tree_scrolled_window; /* not referenced */ gint active_view; guint expanded : 1; @@ -119,6 +121,43 @@ attachment_bar_update_status (EAttachmentBar *bar) } static void +attachment_bar_notify_vadjustment_upper_cb (GObject *object, + GParamSpec *param, + gpointer user_data) +{ + EAttachmentBar *bar = user_data; + GtkAdjustment *adjustment; + gint max_upper, max_content_height = -2; + gint request_height = -1; + + g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); + + adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (bar->priv->icon_scrolled_window)); + max_upper = gtk_adjustment_get_upper (adjustment); + + adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (bar->priv->tree_scrolled_window)); + max_upper = MAX (max_upper, gtk_adjustment_get_upper (adjustment)); + + gtk_widget_style_get (GTK_WIDGET (bar), "max-content-height", &max_content_height, NULL); + + if ((max_content_height >= 0 && max_content_height < 50) || max_content_height <= -2) + max_content_height = 50; + + if (max_content_height == -1) { + request_height = max_upper; + } else if (max_content_height < max_upper) { + request_height = max_content_height; + } else { + request_height = max_upper; + } + + gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (bar->priv->icon_scrolled_window), + request_height); + gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (bar->priv->tree_scrolled_window), + request_height); +} + +static void attachment_bar_set_store (EAttachmentBar *bar, EAttachmentStore *store) { @@ -534,6 +573,10 @@ e_attachment_bar_class_init (EAttachmentBarClass *class) widget_class->button_release_event = attachment_bar_button_release_event; widget_class->motion_notify_event = attachment_bar_motion_notify_event; + #if GTK_CHECK_VERSION (3, 20, 0) + gtk_widget_class_set_css_name (widget_class, G_OBJECT_CLASS_NAME (class)); + #endif + g_object_class_install_property ( object_class, PROP_ACTIVE_VIEW, @@ -574,6 +617,15 @@ e_attachment_bar_class_init (EAttachmentBarClass *class) g_object_class_override_property ( object_class, PROP_EDITABLE, "editable"); + + gtk_widget_class_install_style_property ( + widget_class, + g_param_spec_int ( + "max-content-height", + "Max Content Height", + NULL, + -1, G_MAXINT, 150, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } static void @@ -599,6 +651,7 @@ e_attachment_bar_init (EAttachmentBar *bar) GtkWidget *container; GtkWidget *widget; GtkAction *action; + GtkAdjustment *adjustment; bar->priv = E_ATTACHMENT_BAR_GET_PRIVATE (bar); @@ -627,10 +680,16 @@ e_attachment_bar_init (EAttachmentBar *bar) container = widget; + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (container), widget); + bar->priv->icon_scrolled_window = widget; + gtk_widget_show (widget); + widget = e_attachment_icon_view_new (); gtk_widget_set_can_focus (widget, TRUE); gtk_icon_view_set_model (GTK_ICON_VIEW (widget), bar->priv->model); - gtk_container_add (GTK_CONTAINER (container), widget); + gtk_container_add (GTK_CONTAINER (bar->priv->icon_scrolled_window), widget); bar->priv->icon_view = g_object_ref (widget); gtk_widget_show (widget); @@ -644,10 +703,16 @@ e_attachment_bar_init (EAttachmentBar *bar) container = widget; + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (container), widget); + bar->priv->tree_scrolled_window = widget; + gtk_widget_show (widget); + widget = e_attachment_tree_view_new (); gtk_widget_set_can_focus (widget, TRUE); gtk_tree_view_set_model (GTK_TREE_VIEW (widget), bar->priv->model); - gtk_container_add (GTK_CONTAINER (container), widget); + gtk_container_add (GTK_CONTAINER (bar->priv->tree_scrolled_window), widget); bar->priv->tree_view = g_object_ref (widget); gtk_widget_show (widget); @@ -727,6 +792,14 @@ e_attachment_bar_init (EAttachmentBar *bar) gtk_widget_show (widget); g_object_unref (size_group); + + adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (bar->priv->icon_scrolled_window)); + e_signal_connect_notify (adjustment, "notify::upper", + G_CALLBACK (attachment_bar_notify_vadjustment_upper_cb), bar); + + adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (bar->priv->tree_scrolled_window)); + e_signal_connect_notify (adjustment, "notify::upper", + G_CALLBACK (attachment_bar_notify_vadjustment_upper_cb), bar); } GtkWidget * diff --git a/e-util/e-attachment-button.c b/e-util/e-attachment-button.c deleted file mode 100644 index 090b619..0000000 --- a/e-util/e-attachment-button.c +++ /dev/null @@ -1,929 +0,0 @@ -/* - * e-attachment-button.c - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -/* Much of the popup menu logic here was ripped from GtkMenuToolButton. */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "e-attachment-button.h" -#include "e-misc-utils.h" - -#define E_ATTACHMENT_BUTTON_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonPrivate)) - -struct _EAttachmentButtonPrivate { - - EAttachmentView *view; - EAttachment *attachment; - gulong reference_handler_id; - - GBinding *can_show_binding; - GBinding *shown_binding; - GBinding *zoom_to_window_binding; - - GtkWidget *expand_button; - GtkWidget *toggle_button; - GtkWidget *cell_view; - GtkWidget *popup_menu; - - guint expandable : 1; - guint expanded : 1; - guint zoom_to_window : 1; -}; - -enum { - PROP_0, - PROP_ATTACHMENT, - PROP_EXPANDABLE, - PROP_EXPANDED, - PROP_VIEW, - PROP_ZOOM_TO_WINDOW -}; - -G_DEFINE_TYPE ( - EAttachmentButton, - e_attachment_button, - GTK_TYPE_BOX) - -static void -attachment_button_menu_deactivate_cb (EAttachmentButton *button) -{ - EAttachmentView *view; - GtkActionGroup *action_group; - GtkToggleButton *toggle_button; - - view = e_attachment_button_get_view (button); - action_group = e_attachment_view_get_action_group (view, "inline"); - toggle_button = GTK_TOGGLE_BUTTON (button->priv->toggle_button); - - gtk_toggle_button_set_active (toggle_button, FALSE); - - gtk_action_group_set_visible (action_group, FALSE); -} - -static void -attachment_button_menu_position (GtkMenu *menu, - gint *x, - gint *y, - gboolean *push_in, - EAttachmentButton *button) -{ - GtkRequisition menu_requisition; - GtkTextDirection direction; - GtkAllocation allocation; - GdkRectangle monitor; - GdkScreen *screen; - GdkWindow *window; - GtkWidget *widget; - GtkWidget *toggle_button; - gint monitor_num; - - widget = GTK_WIDGET (button); - toggle_button = button->priv->toggle_button; - gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_requisition, NULL); - - window = gtk_widget_get_parent_window (widget); - screen = gtk_widget_get_screen (GTK_WIDGET (menu)); - monitor_num = gdk_screen_get_monitor_at_window (screen, window); - if (monitor_num < 0) - monitor_num = 0; - gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); - - gtk_widget_get_allocation (widget, &allocation); - - gdk_window_get_origin (window, x, y); - *x += allocation.x; - *y += allocation.y; - - direction = gtk_widget_get_direction (widget); - if (direction == GTK_TEXT_DIR_LTR) - *x += MAX (allocation.width - menu_requisition.width, 0); - else if (menu_requisition.width > allocation.width) - *x -= menu_requisition.width - allocation.width; - - gtk_widget_get_allocation (toggle_button, &allocation); - - if ((*y + allocation.height + - menu_requisition.height) <= monitor.y + monitor.height) - *y += allocation.height; - else if ((*y - menu_requisition.height) >= monitor.y) - *y -= menu_requisition.height; - else if (monitor.y + monitor.height - - (*y + allocation.height) > *y) - *y += allocation.height; - else - *y -= menu_requisition.height; - - *push_in = FALSE; -} - -static void -attachment_button_select_path (EAttachmentButton *button) -{ - EAttachmentView *view; - EAttachment *attachment; - GtkTreeRowReference *reference; - GtkTreePath *path; - - attachment = e_attachment_button_get_attachment (button); - g_return_if_fail (E_IS_ATTACHMENT (attachment)); - - reference = e_attachment_get_reference (attachment); - g_return_if_fail (gtk_tree_row_reference_valid (reference)); - - view = e_attachment_button_get_view (button); - path = gtk_tree_row_reference_get_path (reference); - - e_attachment_view_unselect_all (view); - e_attachment_view_select_path (view, path); - - gtk_tree_path_free (path); -} - -static void -attachment_button_show_popup_menu (EAttachmentButton *button, - GdkEventButton *event) -{ - EAttachmentView *view; - GtkActionGroup *action_group; - GtkToggleButton *toggle_button; - - view = e_attachment_button_get_view (button); - action_group = e_attachment_view_get_action_group (view, "inline"); - toggle_button = GTK_TOGGLE_BUTTON (button->priv->toggle_button); - - attachment_button_select_path (button); - gtk_toggle_button_set_active (toggle_button, TRUE); - - e_attachment_view_show_popup_menu ( - view, event, (GtkMenuPositionFunc) - attachment_button_menu_position, button); - - gtk_action_group_set_visible (action_group, TRUE); -} - -static void -attachment_button_update_cell_view (EAttachmentButton *button) -{ - GtkCellView *cell_view; - EAttachment *attachment; - GtkTreeRowReference *reference; - GtkTreeModel *model = NULL; - GtkTreePath *path = NULL; - - cell_view = GTK_CELL_VIEW (button->priv->cell_view); - - attachment = e_attachment_button_get_attachment (button); - if (attachment == NULL) - goto exit; - - reference = e_attachment_get_reference (attachment); - if (reference == NULL) - goto exit; - - model = gtk_tree_row_reference_get_model (reference); - path = gtk_tree_row_reference_get_path (reference); - -exit: - - gtk_cell_view_set_model (cell_view, model); - if (model) - gtk_cell_view_set_displayed_row (cell_view, path); - - if (path != NULL) - gtk_tree_path_free (path); -} - -static void -attachment_button_update_pixbufs (EAttachmentButton *button) -{ - GtkIconTheme *icon_theme; - GtkCellLayout *cell_layout; - GtkCellRenderer *renderer; - GdkPixbuf *pixbuf_expander_open; - GdkPixbuf *pixbuf_expander_closed; - GList *list; - - icon_theme = gtk_icon_theme_get_default (); - - /* Grab the first cell renderer. */ - cell_layout = GTK_CELL_LAYOUT (button->priv->cell_view); - list = gtk_cell_layout_get_cells (cell_layout); - renderer = GTK_CELL_RENDERER (list->data); - g_list_free (list); - - pixbuf_expander_open = gtk_icon_theme_load_icon ( - icon_theme, "go-down", - GTK_ICON_SIZE_BUTTON, 0, NULL); - - pixbuf_expander_closed = gtk_icon_theme_load_icon ( - icon_theme, "go-next", - GTK_ICON_SIZE_BUTTON, 0, NULL); - - g_object_set ( - renderer, - "pixbuf-expander-open", pixbuf_expander_open, - "pixbuf-expander-closed", pixbuf_expander_closed, - NULL); - - g_object_unref (pixbuf_expander_open); - g_object_unref (pixbuf_expander_closed); -} - -static void -attachment_button_expand_clicked_cb (EAttachmentButton *button) -{ - gboolean expanded; - - expanded = e_attachment_button_get_expanded (button); - e_attachment_button_set_expanded (button, !expanded); -} - -static void -attachment_button_expand_drag_begin_cb (EAttachmentButton *button, - GdkDragContext *context) -{ - EAttachmentView *view; - - view = e_attachment_button_get_view (button); - - attachment_button_select_path (button); - e_attachment_view_drag_begin (view, context); -} - -static void -attachment_button_expand_drag_data_get_cb (EAttachmentButton *button, - GdkDragContext *context, - GtkSelectionData *selection, - guint info, - guint time) -{ - EAttachmentView *view; - EAttachment *attachment; - gchar *mime_type = NULL; - - attachment = e_attachment_button_get_attachment (button); - - if (attachment != NULL) - mime_type = e_attachment_dup_mime_type (attachment); - - if (mime_type != NULL) { - gboolean processed = FALSE; - GdkAtom atom; - gchar *atom_name; - - atom = gtk_selection_data_get_target (selection); - atom_name = gdk_atom_name (atom); - - if (g_strcmp0 (atom_name, mime_type) == 0) { - CamelMimePart *mime_part; - - mime_part = e_attachment_ref_mime_part (attachment); - - if (mime_part != NULL) { - CamelDataWrapper *wrapper; - CamelStream *stream; - GByteArray *buffer; - - buffer = g_byte_array_new (); - stream = camel_stream_mem_new (); - camel_stream_mem_set_byte_array ( - CAMEL_STREAM_MEM (stream), - buffer); - wrapper = camel_medium_get_content ( - CAMEL_MEDIUM (mime_part)); - camel_data_wrapper_decode_to_stream_sync ( - wrapper, stream, NULL, NULL); - g_object_unref (stream); - - gtk_selection_data_set ( - selection, atom, 8, - buffer->data, buffer->len); - processed = TRUE; - - g_byte_array_free (buffer, TRUE); - - g_object_unref (mime_part); - } - } - - g_free (atom_name); - g_free (mime_type); - - if (processed) - return; - } - - view = e_attachment_button_get_view (button); - - e_attachment_view_drag_data_get ( - view, context, selection, info, time); -} - -static void -attachment_button_expand_drag_end_cb (EAttachmentButton *button, - GdkDragContext *context) -{ - EAttachmentView *view; - - view = e_attachment_button_get_view (button); - - e_attachment_view_drag_end (view, context); -} - -static gboolean -attachment_button_toggle_button_press_event_cb (EAttachmentButton *button, - GdkEventButton *event) -{ - if (event->button == 1) { - attachment_button_show_popup_menu (button, event); - return TRUE; - } - - return FALSE; -} - -static void -attachment_button_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_ATTACHMENT: - e_attachment_button_set_attachment ( - E_ATTACHMENT_BUTTON (object), - g_value_get_object (value)); - return; - - case PROP_EXPANDABLE: - e_attachment_button_set_expandable ( - E_ATTACHMENT_BUTTON (object), - g_value_get_boolean (value)); - return; - - case PROP_EXPANDED: - e_attachment_button_set_expanded ( - E_ATTACHMENT_BUTTON (object), - g_value_get_boolean (value)); - return; - - case PROP_VIEW: - e_attachment_button_set_view ( - E_ATTACHMENT_BUTTON (object), - g_value_get_object (value)); - return; - - case PROP_ZOOM_TO_WINDOW: - e_attachment_button_set_zoom_to_window ( - E_ATTACHMENT_BUTTON (object), - g_value_get_boolean (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -attachment_button_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_ATTACHMENT: - g_value_set_object ( - value, - e_attachment_button_get_attachment ( - E_ATTACHMENT_BUTTON (object))); - return; - - case PROP_EXPANDABLE: - g_value_set_boolean ( - value, - e_attachment_button_get_expandable ( - E_ATTACHMENT_BUTTON (object))); - return; - - case PROP_EXPANDED: - g_value_set_boolean ( - value, - e_attachment_button_get_expanded ( - E_ATTACHMENT_BUTTON (object))); - return; - - case PROP_VIEW: - g_value_set_object ( - value, - e_attachment_button_get_view ( - E_ATTACHMENT_BUTTON (object))); - return; - - case PROP_ZOOM_TO_WINDOW: - g_value_set_boolean ( - value, - e_attachment_button_get_zoom_to_window ( - E_ATTACHMENT_BUTTON (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -attachment_button_dispose (GObject *object) -{ - EAttachmentButtonPrivate *priv; - - priv = E_ATTACHMENT_BUTTON_GET_PRIVATE (object); - - if (priv->view != NULL) { - g_object_unref (priv->view); - priv->view = NULL; - } - - if (priv->attachment != NULL) { - g_signal_handler_disconnect ( - priv->attachment, - priv->reference_handler_id); - g_object_unref (priv->attachment); - priv->attachment = NULL; - } - - if (priv->expand_button != NULL) { - g_object_unref (priv->expand_button); - priv->expand_button = NULL; - } - - if (priv->toggle_button != NULL) { - g_object_unref (priv->toggle_button); - priv->toggle_button = NULL; - } - - if (priv->cell_view != NULL) { - g_object_unref (priv->cell_view); - priv->cell_view = NULL; - } - - if (priv->popup_menu != NULL) { - g_signal_handlers_disconnect_matched ( - priv->popup_menu, G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, object); - g_object_unref (priv->popup_menu); - priv->popup_menu = NULL; - } - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_attachment_button_parent_class)->dispose (object); -} - -static void -attachment_button_style_updated (GtkWidget *widget) -{ - EAttachmentButton *button; - - /* Chain up to parent's method. */ - GTK_WIDGET_CLASS (e_attachment_button_parent_class)->style_updated (widget); - - button = E_ATTACHMENT_BUTTON (widget); - attachment_button_update_pixbufs (button); -} - -static void -e_attachment_button_class_init (EAttachmentButtonClass *class) -{ - GObjectClass *object_class; - GtkWidgetClass *widget_class; - - g_type_class_add_private (class, sizeof (EAttachmentButtonPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = attachment_button_set_property; - object_class->get_property = attachment_button_get_property; - object_class->dispose = attachment_button_dispose; - - widget_class = GTK_WIDGET_CLASS (class); - widget_class->style_updated = attachment_button_style_updated; - - g_object_class_install_property ( - object_class, - PROP_ATTACHMENT, - g_param_spec_object ( - "attachment", - "Attachment", - NULL, - E_TYPE_ATTACHMENT, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_EXPANDABLE, - g_param_spec_boolean ( - "expandable", - "Expandable", - NULL, - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); - - g_object_class_install_property ( - object_class, - PROP_EXPANDED, - g_param_spec_boolean ( - "expanded", - "Expanded", - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); - - g_object_class_install_property ( - object_class, - PROP_VIEW, - g_param_spec_object ( - "view", - "View", - NULL, - E_TYPE_ATTACHMENT_VIEW, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_ZOOM_TO_WINDOW, - g_param_spec_boolean ( - "zoom-to-window", - "Zoom to window", - NULL, - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); -} - -static void -e_attachment_button_init (EAttachmentButton *button) -{ - GtkCellRenderer *renderer; - GtkCellLayout *cell_layout; - GtkTargetEntry *targets; - GtkTargetList *list; - GtkWidget *container; - GtkWidget *widget; - GtkStyleContext *context; - gint n_targets; - - button->priv = E_ATTACHMENT_BUTTON_GET_PRIVATE (button); - - gtk_orientable_set_orientation (GTK_ORIENTABLE (button), GTK_ORIENTATION_HORIZONTAL); - - /* Configure Widgets */ - - container = GTK_WIDGET (button); - context = gtk_widget_get_style_context (container); - gtk_style_context_add_class (context, "linked"); - - widget = gtk_button_new (); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - button->priv->expand_button = g_object_ref (widget); - gtk_widget_show (widget); - - e_binding_bind_property ( - button, "expandable", - widget, "sensitive", - G_BINDING_BIDIRECTIONAL | - G_BINDING_SYNC_CREATE); - - widget = gtk_toggle_button_new (); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - button->priv->toggle_button = g_object_ref (widget); - gtk_widget_show (widget); - - container = button->priv->expand_button; - - widget = gtk_cell_view_new (); - gtk_container_add (GTK_CONTAINER (container), widget); - button->priv->cell_view = g_object_ref (widget); - gtk_widget_show (widget); - - container = button->priv->toggle_button; - - widget = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); - gtk_container_add (GTK_CONTAINER (container), widget); - gtk_widget_show (widget); - - /* Configure Renderers */ - - cell_layout = GTK_CELL_LAYOUT (button->priv->cell_view); - - renderer = gtk_cell_renderer_pixbuf_new (); - g_object_set (renderer, "is-expander", TRUE, NULL); - gtk_cell_layout_pack_start (cell_layout, renderer, FALSE); - - e_binding_bind_property ( - button, "expanded", - renderer, "is-expanded", - G_BINDING_BIDIRECTIONAL | - G_BINDING_SYNC_CREATE); - - renderer = gtk_cell_renderer_pixbuf_new (); - g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL); - gtk_cell_layout_pack_start (cell_layout, renderer, FALSE); - - gtk_cell_layout_add_attribute ( - cell_layout, renderer, "gicon", - E_ATTACHMENT_STORE_COLUMN_ICON); - - /* Configure Drag and Drop */ - - list = gtk_target_list_new (NULL, 0); - gtk_target_list_add_uri_targets (list, 0); - targets = gtk_target_table_new_from_list (list, &n_targets); - - gtk_drag_source_set ( - button->priv->expand_button, GDK_BUTTON1_MASK, - targets, n_targets, GDK_ACTION_COPY); - - gtk_drag_source_set ( - button->priv->toggle_button, GDK_BUTTON1_MASK, - targets, n_targets, GDK_ACTION_COPY); - - gtk_target_table_free (targets, n_targets); - gtk_target_list_unref (list); - - /* Configure Signal Handlers */ - - g_signal_connect_swapped ( - button->priv->expand_button, "clicked", - G_CALLBACK (attachment_button_expand_clicked_cb), button); - - g_signal_connect_swapped ( - button->priv->expand_button, "drag-begin", - G_CALLBACK (attachment_button_expand_drag_begin_cb), - button); - - g_signal_connect_swapped ( - button->priv->expand_button, "drag-data-get", - G_CALLBACK (attachment_button_expand_drag_data_get_cb), - button); - - g_signal_connect_swapped ( - button->priv->expand_button, "drag-end", - G_CALLBACK (attachment_button_expand_drag_end_cb), - button); - - g_signal_connect_swapped ( - button->priv->toggle_button, "button-press-event", - G_CALLBACK (attachment_button_toggle_button_press_event_cb), - button); - - g_signal_connect_swapped ( - button->priv->toggle_button, "drag-begin", - G_CALLBACK (attachment_button_expand_drag_begin_cb), - button); - - g_signal_connect_swapped ( - button->priv->toggle_button, "drag-data-get", - G_CALLBACK (attachment_button_expand_drag_data_get_cb), - button); - - g_signal_connect_swapped ( - button->priv->toggle_button, "drag-end", - G_CALLBACK (attachment_button_expand_drag_end_cb), - button); -} - -GtkWidget * -e_attachment_button_new (void) -{ - return g_object_new ( - E_TYPE_ATTACHMENT_BUTTON, NULL); -} - -EAttachmentView * -e_attachment_button_get_view (EAttachmentButton *button) -{ - g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), NULL); - - return button->priv->view; -} - -void -e_attachment_button_set_view (EAttachmentButton *button, - EAttachmentView *view) -{ - GtkWidget *popup_menu; - - g_return_if_fail (button->priv->view == NULL); - - g_object_ref (view); - if (button->priv->view) - g_object_unref (button->priv->view); - button->priv->view = view; - - popup_menu = e_attachment_view_get_popup_menu (view); - - g_signal_connect_swapped ( - popup_menu, "deactivate", - G_CALLBACK (attachment_button_menu_deactivate_cb), button); - - /* Keep a reference to the popup menu so we can - * disconnect the signal handler in dispose(). */ - if (button->priv->popup_menu) - g_object_unref (button->priv->popup_menu); - button->priv->popup_menu = g_object_ref (popup_menu); -} - -EAttachment * -e_attachment_button_get_attachment (EAttachmentButton *button) -{ - g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), NULL); - - return button->priv->attachment; -} - -void -e_attachment_button_set_attachment (EAttachmentButton *button, - EAttachment *attachment) -{ - GtkTargetEntry *targets; - GtkTargetList *list; - gint n_targets; - - g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button)); - - if (attachment != NULL) { - g_return_if_fail (E_IS_ATTACHMENT (attachment)); - g_object_ref (attachment); - } - - if (button->priv->attachment != NULL) { - g_clear_object (&button->priv->can_show_binding); - g_clear_object (&button->priv->shown_binding); - g_clear_object (&button->priv->zoom_to_window_binding); - g_signal_handler_disconnect ( - button->priv->attachment, - button->priv->reference_handler_id); - g_object_unref (button->priv->attachment); - } - - button->priv->attachment = attachment; - - if (attachment != NULL) { - GBinding *binding; - gulong handler_id; - - binding = e_binding_bind_property ( - attachment, "can-show", - button, "expandable", - G_BINDING_BIDIRECTIONAL | - G_BINDING_SYNC_CREATE); - button->priv->can_show_binding = binding; - - binding = e_binding_bind_property ( - attachment, "shown", - button, "expanded", - G_BINDING_BIDIRECTIONAL | - G_BINDING_SYNC_CREATE); - button->priv->shown_binding = binding; - - handler_id = g_signal_connect_swapped ( - attachment, "notify::reference", - G_CALLBACK (attachment_button_update_cell_view), - button); - button->priv->reference_handler_id = handler_id; - - binding = e_binding_bind_property ( - attachment, "zoom-to-window", - button, "zoom-to-window", - G_BINDING_BIDIRECTIONAL | - G_BINDING_SYNC_CREATE); - button->priv->zoom_to_window_binding = binding; - - attachment_button_update_cell_view (button); - attachment_button_update_pixbufs (button); - } - - /* update drag sources */ - list = gtk_target_list_new (NULL, 0); - gtk_target_list_add_uri_targets (list, 0); - - if (attachment != NULL) { - gchar *simple_type; - - simple_type = e_attachment_dup_mime_type (attachment); - if (simple_type != NULL) { - GtkTargetEntry attach_entry[] = { { NULL, 0, 2 } }; - - attach_entry[0].target = simple_type; - - gtk_target_list_add_table ( - list, attach_entry, - G_N_ELEMENTS (attach_entry)); - - g_free (simple_type); - } - } - - targets = gtk_target_table_new_from_list (list, &n_targets); - - gtk_drag_source_set ( - button->priv->expand_button, GDK_BUTTON1_MASK, - targets, n_targets, GDK_ACTION_COPY); - - gtk_drag_source_set ( - button->priv->toggle_button, GDK_BUTTON1_MASK, - targets, n_targets, GDK_ACTION_COPY); - - gtk_target_table_free (targets, n_targets); - gtk_target_list_unref (list); - - g_object_notify (G_OBJECT (button), "attachment"); -} - -gboolean -e_attachment_button_get_expandable (EAttachmentButton *button) -{ - g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE); - - return button->priv->expandable; -} - -void -e_attachment_button_set_expandable (EAttachmentButton *button, - gboolean expandable) -{ - g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button)); - - if (button->priv->expandable == expandable) - return; - - button->priv->expandable = expandable; - - if (!expandable) - e_attachment_button_set_expanded (button, FALSE); - - g_object_notify (G_OBJECT (button), "expandable"); -} - -gboolean -e_attachment_button_get_expanded (EAttachmentButton *button) -{ - g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE); - - return button->priv->expanded; -} - -void -e_attachment_button_set_expanded (EAttachmentButton *button, - gboolean expanded) -{ - g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button)); - - if (button->priv->expanded == expanded) - return; - - button->priv->expanded = expanded; - - g_object_notify (G_OBJECT (button), "expanded"); -} - -gboolean -e_attachment_button_get_zoom_to_window (EAttachmentButton *button) -{ - g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE); - - return button->priv->zoom_to_window; -} - -void -e_attachment_button_set_zoom_to_window (EAttachmentButton *button, - gboolean zoom_to_window) -{ - g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button)); - - if ((button->priv->zoom_to_window ? 1 : 0) == (zoom_to_window ? 1 : 0)) - return; - - button->priv->zoom_to_window = zoom_to_window; - - g_object_notify (G_OBJECT (button), "zoom-to-window"); -} diff --git a/e-util/e-attachment-button.h b/e-util/e-attachment-button.h deleted file mode 100644 index 1508473..0000000 --- a/e-util/e-attachment-button.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * e-attachment-button.h - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION) -#error "Only <e-util/e-util.h> should be included directly." -#endif - -#ifndef E_ATTACHMENT_BUTTON_H -#define E_ATTACHMENT_BUTTON_H - -#include <gtk/gtk.h> -#include <e-util/e-attachment.h> -#include <e-util/e-attachment-view.h> - -/* Standard GObject macros */ -#define E_TYPE_ATTACHMENT_BUTTON \ - (e_attachment_button_get_type ()) -#define E_ATTACHMENT_BUTTON(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButton)) -#define E_ATTACHMENT_BUTTON_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonClass)) -#define E_IS_ATTACHMENT_BUTTON(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_ATTACHMENT_BUTTON)) -#define E_IS_ATTACHMENT_BUTTON_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_ATTACHMENT_BUTTON)) -#define E_ATTACHMENT_BUTTON_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonClass)) - -G_BEGIN_DECLS - -typedef struct _EAttachmentButton EAttachmentButton; -typedef struct _EAttachmentButtonClass EAttachmentButtonClass; -typedef struct _EAttachmentButtonPrivate EAttachmentButtonPrivate; - -struct _EAttachmentButton { - GtkBox parent; - EAttachmentButtonPrivate *priv; -}; - -struct _EAttachmentButtonClass { - GtkBoxClass parent_class; -}; - -GType e_attachment_button_get_type (void) G_GNUC_CONST; -GtkWidget * e_attachment_button_new (void); -EAttachmentView * - e_attachment_button_get_view (EAttachmentButton *button); -void e_attachment_button_set_view (EAttachmentButton *button, - EAttachmentView *view); -EAttachment * e_attachment_button_get_attachment - (EAttachmentButton *button); -void e_attachment_button_set_attachment - (EAttachmentButton *button, - EAttachment *attachment); -gboolean e_attachment_button_get_expandable - (EAttachmentButton *button); -void e_attachment_button_set_expandable - (EAttachmentButton *button, - gboolean expandable); -gboolean e_attachment_button_get_expanded - (EAttachmentButton *button); -void e_attachment_button_set_expanded - (EAttachmentButton *button, - gboolean expanded); -gboolean e_attachment_button_get_zoom_to_window - (EAttachmentButton *button); -void e_attachment_button_set_zoom_to_window - (EAttachmentButton *button, - gboolean zoom_to_window); - -G_END_DECLS - -#endif /* E_ATTACHMENT_BUTTON_H */ diff --git a/e-util/e-attachment-store.c b/e-util/e-attachment-store.c index f0648db..85fa19a 100644 --- a/e-util/e-attachment-store.c +++ b/e-util/e-attachment-store.c @@ -53,12 +53,157 @@ enum { PROP_TOTAL_SIZE }; +enum { + ATTACHMENT_ADDED, + ATTACHMENT_REMOVED, + LAST_SIGNAL +}; + +static gulong signals[LAST_SIGNAL]; + G_DEFINE_TYPE ( EAttachmentStore, e_attachment_store, GTK_TYPE_LIST_STORE) static void +attachment_store_update_file_info_cb (EAttachment *attachment, + const gchar *caption, + const gchar *content_type, + const gchar *description, + gint64 size, + gpointer user_data) +{ + EAttachmentStore *store = user_data; + GtkTreeIter iter; + + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + + if (e_attachment_store_find_attachment_iter (store, attachment, &iter)) { + gtk_list_store_set ( + GTK_LIST_STORE (store), &iter, + E_ATTACHMENT_STORE_COLUMN_CAPTION, caption, + E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_type, + E_ATTACHMENT_STORE_COLUMN_DESCRIPTION, description, + E_ATTACHMENT_STORE_COLUMN_SIZE, size, + -1); + } +} + +static void +attachment_store_update_icon_cb (EAttachment *attachment, + GIcon *icon, + gpointer user_data) +{ + EAttachmentStore *store = user_data; + GtkTreeIter iter; + + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + + if (e_attachment_store_find_attachment_iter (store, attachment, &iter)) { + gtk_list_store_set ( + GTK_LIST_STORE (store), &iter, + E_ATTACHMENT_STORE_COLUMN_ICON, icon, + -1); + } +} + +static void +attachment_store_update_progress_cb (EAttachment *attachment, + gboolean loading, + gboolean saving, + gint percent, + gpointer user_data) +{ + EAttachmentStore *store = user_data; + GtkTreeIter iter; + + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + + if (e_attachment_store_find_attachment_iter (store, attachment, &iter)) { + gtk_list_store_set ( + GTK_LIST_STORE (store), &iter, + E_ATTACHMENT_STORE_COLUMN_LOADING, loading, + E_ATTACHMENT_STORE_COLUMN_SAVING, saving, + E_ATTACHMENT_STORE_COLUMN_PERCENT, percent, + -1); + } +} + +static void +attachment_store_load_failed_cb (EAttachment *attachment, + gpointer user_data) +{ + EAttachmentStore *store = user_data; + + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + + e_attachment_store_remove_attachment (store, attachment); +} + +static void +attachment_store_attachment_notify_cb (GObject *attachment, + GParamSpec *param, + gpointer user_data) +{ + EAttachmentStore *store = user_data; + + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (param != NULL); + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + + if (g_str_equal (param->name, "loading")) { + g_object_notify (G_OBJECT (store), "num-loading"); + } else if (g_str_equal (param->name, "file-info")) { + g_object_notify (G_OBJECT (store), "total-size"); + } +} + +static void +attachment_store_attachment_added (EAttachmentStore *store, + EAttachment *attachment) +{ + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + g_signal_connect (attachment, "update-file-info", + G_CALLBACK (attachment_store_update_file_info_cb), store); + g_signal_connect (attachment, "update-icon", + G_CALLBACK (attachment_store_update_icon_cb), store); + g_signal_connect (attachment, "update-progress", + G_CALLBACK (attachment_store_update_progress_cb), store); + g_signal_connect (attachment, "load-failed", + G_CALLBACK (attachment_store_load_failed_cb), store); + g_signal_connect (attachment, "notify", + G_CALLBACK (attachment_store_attachment_notify_cb), store); + + e_attachment_update_store_columns (attachment); +} + +static void +attachment_store_attachment_removed (EAttachmentStore *store, + EAttachment *attachment) +{ + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + g_signal_handlers_disconnect_by_func (attachment, + G_CALLBACK (attachment_store_update_file_info_cb), store); + g_signal_handlers_disconnect_by_func (attachment, + G_CALLBACK (attachment_store_update_icon_cb), store); + g_signal_handlers_disconnect_by_func (attachment, + G_CALLBACK (attachment_store_update_progress_cb), store); + g_signal_handlers_disconnect_by_func (attachment, + G_CALLBACK (attachment_store_load_failed_cb), store); + g_signal_handlers_disconnect_by_func (attachment, + G_CALLBACK (attachment_store_attachment_notify_cb), store); +} + +static void attachment_store_get_property (GObject *object, guint property_id, GValue *value, @@ -124,6 +269,9 @@ e_attachment_store_class_init (EAttachmentStoreClass *class) object_class->dispose = attachment_store_dispose; object_class->finalize = attachment_store_finalize; + class->attachment_added = attachment_store_attachment_added; + class->attachment_removed = attachment_store_attachment_removed; + g_object_class_install_property ( object_class, PROP_NUM_ATTACHMENTS, @@ -159,6 +307,22 @@ e_attachment_store_class_init (EAttachmentStoreClass *class) G_MAXUINT64, 0, G_PARAM_READABLE)); + + signals[ATTACHMENT_ADDED] = g_signal_new ( + "attachment-added", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAttachmentStoreClass, attachment_added), + NULL, NULL, NULL, + G_TYPE_NONE, 1, E_TYPE_ATTACHMENT); + + signals[ATTACHMENT_REMOVED] = g_signal_new ( + "attachment-removed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAttachmentStoreClass, attachment_removed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, E_TYPE_ATTACHMENT); } static void @@ -225,13 +389,12 @@ e_attachment_store_add_attachment (EAttachmentStore *store, store->priv->attachment_index, g_object_ref (attachment), reference); - /* This lets the attachment tell us when to update. */ - e_attachment_set_reference (attachment, reference); - g_object_freeze_notify (G_OBJECT (store)); g_object_notify (G_OBJECT (store), "num-attachments"); g_object_notify (G_OBJECT (store), "total-size"); g_object_thaw_notify (G_OBJECT (store)); + + g_signal_emit (store, signals[ATTACHMENT_ADDED], 0, attachment); } gboolean @@ -243,6 +406,7 @@ e_attachment_store_remove_attachment (EAttachmentStore *store, GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; + gboolean removed; g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE); g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); @@ -254,12 +418,12 @@ e_attachment_store_remove_attachment (EAttachmentStore *store, return FALSE; if (!gtk_tree_row_reference_valid (reference)) { - g_hash_table_remove (hash_table, attachment); + if (g_hash_table_remove (hash_table, attachment)) + g_signal_emit (store, signals[ATTACHMENT_REMOVED], 0, attachment); return FALSE; } e_attachment_cancel (attachment); - e_attachment_set_reference (attachment, NULL); model = gtk_tree_row_reference_get_model (reference); path = gtk_tree_row_reference_get_path (reference); @@ -267,13 +431,16 @@ e_attachment_store_remove_attachment (EAttachmentStore *store, gtk_tree_path_free (path); gtk_list_store_remove (GTK_LIST_STORE (store), &iter); - g_hash_table_remove (hash_table, attachment); + removed = g_hash_table_remove (hash_table, attachment); g_object_freeze_notify (G_OBJECT (store)); g_object_notify (G_OBJECT (store), "num-attachments"); g_object_notify (G_OBJECT (store), "total-size"); g_object_thaw_notify (G_OBJECT (store)); + if (removed) + g_signal_emit (store, signals[ATTACHMENT_REMOVED], 0, attachment); + return TRUE; } @@ -304,9 +471,10 @@ e_attachment_store_remove_all (EAttachmentStore *store) EAttachment *attachment = iter->data; e_attachment_cancel (attachment); - e_attachment_set_reference (attachment, NULL); g_warn_if_fail (g_hash_table_remove (store->priv->attachment_index, attachment)); + + g_signal_emit (store, signals[ATTACHMENT_REMOVED], 0, attachment); } g_list_foreach (list, (GFunc) g_object_unref, NULL); @@ -780,6 +948,51 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store, return destination; } +gboolean +e_attachment_store_transform_num_attachments_to_visible_boolean (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + g_return_val_if_fail (from_value != NULL, FALSE); + g_return_val_if_fail (to_value != NULL, FALSE); + g_return_val_if_fail (G_VALUE_HOLDS_UINT (from_value), FALSE); + g_return_val_if_fail (G_VALUE_HOLDS_BOOLEAN (to_value), FALSE); + + g_value_set_boolean (to_value, g_value_get_uint (from_value) != 0); + + return TRUE; +} + +gboolean +e_attachment_store_find_attachment_iter (EAttachmentStore *store, + EAttachment *attachment, + GtkTreeIter *out_iter) +{ + GtkTreeRowReference *reference; + GtkTreeModel *model; + GtkTreePath *path; + gboolean found; + + g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE); + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); + g_return_val_if_fail (out_iter != NULL, FALSE); + + reference = g_hash_table_lookup (store->priv->attachment_index, attachment); + + if (!reference || !gtk_tree_row_reference_valid (reference)) + return FALSE; + + model = gtk_tree_row_reference_get_model (reference); + g_return_val_if_fail (model == GTK_TREE_MODEL (store), FALSE); + + path = gtk_tree_row_reference_get_path (reference); + found = gtk_tree_model_get_iter (model, out_iter, path); + gtk_tree_path_free (path); + + return found; +} + /******************** e_attachment_store_get_uris_async() ********************/ typedef struct _UriContext UriContext; diff --git a/e-util/e-attachment-store.h b/e-util/e-attachment-store.h index 584110c..1f15f5d 100644 --- a/e-util/e-attachment-store.h +++ b/e-util/e-attachment-store.h @@ -60,6 +60,12 @@ struct _EAttachmentStore { struct _EAttachmentStoreClass { GtkListStoreClass parent_class; + + /* Signals */ + void (* attachment_added) (EAttachmentStore *store, + EAttachment *attachment); + void (* attachment_removed) (EAttachmentStore *store, + EAttachment *attachment); }; enum { @@ -104,6 +110,15 @@ GFile * e_attachment_store_run_save_dialog GList *attachment_list, GtkWindow *parent); +gboolean e_attachment_store_transform_num_attachments_to_visible_boolean + (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data); +gboolean e_attachment_store_find_attachment_iter + (EAttachmentStore *store, + EAttachment *attachment, + GtkTreeIter *out_iter); /* Asynchronous Operations */ void e_attachment_store_get_uris_async (EAttachmentStore *store, diff --git a/e-util/e-attachment-view.c b/e-util/e-attachment-view.c index 7020ab8..00b17df 100644 --- a/e-util/e-attachment-view.c +++ b/e-util/e-attachment-view.c @@ -50,15 +50,7 @@ static const gchar *ui = " <menuitem action='remove'/>" " <menuitem action='properties'/>" " <separator/>" -" <placeholder name='inline-actions'>" -" <menuitem action='zoom-to-100'/>" -" <menuitem action='zoom-to-window'/>" -" <menuitem action='show'/>" -" <menuitem action='show-all'/>" -" <separator/>" -" <menuitem action='hide'/>" -" <menuitem action='hide-all'/>" -" </placeholder>" +" <placeholder name='inline-actions'/>" " <separator/>" " <placeholder name='custom-actions'/>" " <separator/>" @@ -108,44 +100,6 @@ action_cancel_cb (GtkAction *action, } static void -action_hide_cb (GtkAction *action, - EAttachmentView *view) -{ - EAttachment *attachment; - GList *list; - - list = e_attachment_view_get_selected_attachments (view); - g_return_if_fail (g_list_length (list) == 1); - attachment = list->data; - - e_attachment_set_shown (attachment, FALSE); - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); -} - -static void -action_hide_all_cb (GtkAction *action, - EAttachmentView *view) -{ - EAttachmentStore *store; - GList *list, *iter; - - store = e_attachment_view_get_store (view); - list = e_attachment_store_get_attachments (store); - - for (iter = list; iter != NULL; iter = iter->next) { - EAttachment *attachment; - - attachment = E_ATTACHMENT (iter->data); - e_attachment_set_shown (attachment, FALSE); - } - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); -} - -static void action_open_with_cb (GtkAction *action, EAttachmentView *view) { @@ -342,78 +296,6 @@ exit: g_list_free (list); } -static void -action_show_cb (GtkAction *action, - EAttachmentView *view) -{ - EAttachment *attachment; - GList *list; - - list = e_attachment_view_get_selected_attachments (view); - g_return_if_fail (g_list_length (list) == 1); - attachment = list->data; - - e_attachment_set_shown (attachment, TRUE); - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); -} - -static void -action_show_all_cb (GtkAction *action, - EAttachmentView *view) -{ - EAttachmentStore *store; - GList *list, *iter; - - store = e_attachment_view_get_store (view); - list = e_attachment_store_get_attachments (store); - - for (iter = list; iter != NULL; iter = iter->next) { - EAttachment *attachment; - - attachment = E_ATTACHMENT (iter->data); - e_attachment_set_shown (attachment, TRUE); - } - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); -} - -static void -action_zoom_to_100_cb (GtkAction *action, - EAttachmentView *view) -{ - EAttachment *attachment; - GList *list; - - list = e_attachment_view_get_selected_attachments (view); - g_return_if_fail (g_list_length (list) == 1); - attachment = list->data; - - e_attachment_set_zoom_to_window (attachment, FALSE); - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); -} - -static void -action_zoom_to_window_cb (GtkAction *action, - EAttachmentView *view) -{ - EAttachment *attachment; - GList *list; - - list = e_attachment_view_get_selected_attachments (view); - g_return_if_fail (g_list_length (list) == 1); - attachment = list->data; - - e_attachment_set_zoom_to_window (attachment, TRUE); - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); -} - static GtkActionEntry standard_entries[] = { { "cancel", @@ -478,51 +360,6 @@ static GtkActionEntry editable_entries[] = { G_CALLBACK (action_remove_cb) } }; -static GtkActionEntry inline_entries[] = { - - { "hide", - NULL, - N_("_Hide"), - NULL, - NULL, /* XXX Add a tooltip! */ - G_CALLBACK (action_hide_cb) }, - - { "hide-all", - NULL, - N_("Hid_e All"), - NULL, - NULL, /* XXX Add a tooltip! */ - G_CALLBACK (action_hide_all_cb) }, - - { "show", - NULL, - N_("_View Inline"), - NULL, - NULL, /* XXX Add a tooltip! */ - G_CALLBACK (action_show_cb) }, - - { "show-all", - NULL, - N_("Vie_w All Inline"), - NULL, - NULL, /* XXX Add a tooltip! */ - G_CALLBACK (action_show_all_cb) }, - - { "zoom-to-100", - NULL, - N_("_Zoom to 100%"), - NULL, - N_("Zoom the image to its natural size"), - G_CALLBACK (action_zoom_to_100_cb) }, - - { "zoom-to-window", - NULL, - N_("_Zoom to window"), - NULL, - N_("Zoom large images to not be wider than the window width"), - G_CALLBACK (action_zoom_to_window_cb) } -}; - static void call_attachment_load_handle_error (GObject *source_object, GAsyncResult *result, @@ -745,76 +582,32 @@ attachment_view_update_actions (EAttachmentView *view) { EAttachmentViewPrivate *priv; EAttachment *attachment; - EAttachmentStore *store; GtkActionGroup *action_group; GtkAction *action; GList *list, *iter; - guint n_shown = 0; - guint n_hidden = 0; guint n_selected; gboolean busy = FALSE; - gboolean can_show = FALSE; - gboolean shown = FALSE; - gboolean is_image = FALSE; - gboolean zoom_to_window = FALSE; - gboolean visible; g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); priv = e_attachment_view_get_private (view); - store = e_attachment_view_get_store (view); - list = e_attachment_store_get_attachments (store); - - for (iter = list; iter != NULL; iter = iter->next) { - attachment = iter->data; - - if (!e_attachment_get_can_show (attachment)) - continue; - - if (e_attachment_get_shown (attachment)) - n_shown++; - else - n_hidden++; - } - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); - list = e_attachment_view_get_selected_attachments (view); n_selected = g_list_length (list); if (n_selected == 1) { - gchar *mime_type; - attachment = g_object_ref (list->data); - mime_type = e_attachment_dup_mime_type (attachment); + busy |= e_attachment_get_loading (attachment); busy |= e_attachment_get_saving (attachment); - can_show = e_attachment_get_can_show (attachment); - shown = e_attachment_get_shown (attachment); - zoom_to_window = e_attachment_get_zoom_to_window (attachment); - is_image = can_show && mime_type && g_ascii_strncasecmp (mime_type, "image/", 6) == 0; - - g_free (mime_type); } else attachment = NULL; - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); + g_list_free_full (list, g_object_unref); action = e_attachment_view_get_action (view, "cancel"); gtk_action_set_visible (action, busy); - action = e_attachment_view_get_action (view, "hide"); - gtk_action_set_visible (action, can_show && shown); - - /* Show this action if there are multiple viewable - * attachments, and at least one of them is shown. */ - visible = (n_shown + n_hidden > 1) && (n_shown > 0); - action = e_attachment_view_get_action (view, "hide-all"); - gtk_action_set_visible (action, visible); - action = e_attachment_view_get_action (view, "open-with"); gtk_action_set_visible (action, !busy && n_selected == 1); @@ -827,29 +620,16 @@ attachment_view_update_actions (EAttachmentView *view) action = e_attachment_view_get_action (view, "save-as"); gtk_action_set_visible (action, !busy && n_selected > 0); - action = e_attachment_view_get_action (view, "show"); - gtk_action_set_visible (action, can_show && !shown); - - action = e_attachment_view_get_action (view, "zoom-to-100"); - gtk_action_set_visible (action, can_show && shown && is_image && zoom_to_window); - - action = e_attachment_view_get_action (view, "zoom-to-window"); - gtk_action_set_visible (action, can_show && shown && is_image && !zoom_to_window); - - /* Show this action if there are multiple viewable - * attachments, and at least one of them is hidden. */ - visible = (n_shown + n_hidden > 1) && (n_hidden > 0); - action = e_attachment_view_get_action (view, "show-all"); - gtk_action_set_visible (action, visible); - /* Clear out the "openwith" action group. */ gtk_ui_manager_remove_ui (priv->ui_manager, priv->merge_id); action_group = e_attachment_view_get_action_group (view, "openwith"); e_action_group_remove_all_actions (action_group); gtk_ui_manager_ensure_update (priv->ui_manager); - if (attachment == NULL || busy) + if (!attachment || busy) { + g_clear_object (&attachment); return; + } list = e_attachment_list_apps (attachment); @@ -913,9 +693,8 @@ attachment_view_update_actions (EAttachmentView *view) g_free (action_tooltip); } + g_list_free_full (list, g_object_unref); g_object_unref (attachment); - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); } static void @@ -1005,13 +784,6 @@ e_attachment_view_init (EAttachmentView *view) action_group, editable_entries, G_N_ELEMENTS (editable_entries), view); - action_group = e_attachment_view_add_action_group (view, "inline"); - - gtk_action_group_add_actions ( - action_group, inline_entries, - G_N_ELEMENTS (inline_entries), view); - gtk_action_group_set_visible (action_group, FALSE); - e_attachment_view_add_action_group (view, "openwith"); /* Because we are loading from a hard-coded string, there is diff --git a/e-util/e-attachment.c b/e-util/e-attachment.c index f9f8e9b..d451f18 100644 --- a/e-util/e-attachment.c +++ b/e-util/e-attachment.c @@ -35,7 +35,6 @@ #include <libedataserver/libedataserver.h> -#include "e-attachment-store.h" #include "e-icon-factory.h" #include "e-mktemp.h" #include "e-misc-utils.h" @@ -77,8 +76,7 @@ struct _EAttachmentPrivate { guint can_show : 1; guint loading : 1; guint saving : 1; - guint shown : 1; - guint zoom_to_window : 1; + guint initially_shown : 1; guint save_self : 1; guint save_extracted : 1; @@ -86,12 +84,6 @@ struct _EAttachmentPrivate { CamelCipherValidityEncrypt encrypted; CamelCipherValiditySign signed_; - /* This is a reference to our row in an EAttachmentStore, - * serving as a means of broadcasting "row-changed" signals. - * If we are removed from the store, we lazily free the - * reference when it is found to be to be invalid. */ - GtkTreeRowReference *reference; - /* These are IDs for idle callbacks, * protected by the idle_lock mutex. */ GMutex idle_lock; @@ -111,15 +103,23 @@ enum { PROP_LOADING, PROP_MIME_PART, PROP_PERCENT, - PROP_REFERENCE, PROP_SAVE_SELF, PROP_SAVE_EXTRACTED, PROP_SAVING, - PROP_SHOWN, - PROP_SIGNED, - PROP_ZOOM_TO_WINDOW + PROP_INITIALLY_SHOWN, + PROP_SIGNED +}; + +enum { + LOAD_FAILED, + UPDATE_FILE_INFO, + UPDATE_ICON, + UPDATE_PROGRESS, + LAST_SIGNAL }; +static guint signals[LAST_SIGNAL]; + G_DEFINE_TYPE ( EAttachment, e_attachment, @@ -250,10 +250,6 @@ static gboolean attachment_update_file_info_columns_idle_cb (gpointer weak_ref) { EAttachment *attachment; - GtkTreeRowReference *reference; - GtkTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; GFileInfo *file_info; const gchar *content_type; const gchar *display_name; @@ -271,19 +267,10 @@ attachment_update_file_info_columns_idle_cb (gpointer weak_ref) attachment->priv->update_file_info_columns_idle_id = 0; g_mutex_unlock (&attachment->priv->idle_lock); - reference = e_attachment_get_reference (attachment); - if (!gtk_tree_row_reference_valid (reference)) - goto exit; - file_info = e_attachment_ref_file_info (attachment); if (file_info == NULL) goto exit; - model = gtk_tree_row_reference_get_model (reference); - path = gtk_tree_row_reference_get_path (reference); - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_path_free (path); - content_type = g_file_info_get_content_type (file_info); display_name = g_file_info_get_display_name (file_info); size = g_file_info_get_size (file_info); @@ -298,18 +285,11 @@ attachment_update_file_info_columns_idle_cb (gpointer weak_ref) } if (size > 0) - caption = g_strdup_printf ( - "%s\n(%s)", description, display_size); + caption = g_strdup_printf ("%s\n(%s)", description, display_size); else caption = g_strdup (description); - gtk_list_store_set ( - GTK_LIST_STORE (model), &iter, - E_ATTACHMENT_STORE_COLUMN_CAPTION, caption, - E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_desc, - E_ATTACHMENT_STORE_COLUMN_DESCRIPTION, description, - E_ATTACHMENT_STORE_COLUMN_SIZE, size, - -1); + g_signal_emit (attachment, signals[UPDATE_FILE_INFO], 0, caption, content_desc, description, (gint64) size); g_free (content_desc); g_free (display_size); @@ -347,10 +327,6 @@ static gboolean attachment_update_icon_column_idle_cb (gpointer weak_ref) { EAttachment *attachment; - GtkTreeRowReference *reference; - GtkTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; GFileInfo *file_info; GCancellable *cancellable; GIcon *icon = NULL; @@ -365,15 +341,6 @@ attachment_update_icon_column_idle_cb (gpointer weak_ref) attachment->priv->update_icon_column_idle_id = 0; g_mutex_unlock (&attachment->priv->idle_lock); - reference = e_attachment_get_reference (attachment); - if (!gtk_tree_row_reference_valid (reference)) - goto exit; - - model = gtk_tree_row_reference_get_model (reference); - path = gtk_tree_row_reference_get_path (reference); - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_path_free (path); - cancellable = attachment->priv->cancellable; file_info = e_attachment_ref_file_info (attachment); @@ -475,10 +442,7 @@ attachment_update_icon_column_idle_cb (gpointer weak_ref) icon = emblemed_icon; } - gtk_list_store_set ( - GTK_LIST_STORE (model), &iter, - E_ATTACHMENT_STORE_COLUMN_ICON, icon, - -1); + g_signal_emit (attachment, signals[UPDATE_ICON], 0, icon); /* Cache the icon to reuse for things like drag-n-drop. */ if (attachment->priv->icon != NULL) @@ -517,10 +481,6 @@ static gboolean attachment_update_progress_columns_idle_cb (gpointer weak_ref) { EAttachment *attachment; - GtkTreeRowReference *reference; - GtkTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; gboolean loading; gboolean saving; gint percent; @@ -533,26 +493,12 @@ attachment_update_progress_columns_idle_cb (gpointer weak_ref) attachment->priv->update_progress_columns_idle_id = 0; g_mutex_unlock (&attachment->priv->idle_lock); - reference = e_attachment_get_reference (attachment); - if (!gtk_tree_row_reference_valid (reference)) - goto exit; - - model = gtk_tree_row_reference_get_model (reference); - path = gtk_tree_row_reference_get_path (reference); - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_path_free (path); - /* Don't show progress bars until we have progress to report. */ percent = e_attachment_get_percent (attachment); loading = e_attachment_get_loading (attachment) && (percent > 0); saving = e_attachment_get_saving (attachment) && (percent > 0); - gtk_list_store_set ( - GTK_LIST_STORE (model), &iter, - E_ATTACHMENT_STORE_COLUMN_LOADING, loading, - E_ATTACHMENT_STORE_COLUMN_PERCENT, percent, - E_ATTACHMENT_STORE_COLUMN_SAVING, saving, - -1); + g_signal_emit (attachment, signals[UPDATE_PROGRESS], 0, loading, saving, percent); exit: g_clear_object (&attachment); @@ -583,10 +529,6 @@ static void attachment_set_loading (EAttachment *attachment, gboolean loading) { - GtkTreeRowReference *reference; - - reference = e_attachment_get_reference (attachment); - attachment->priv->percent = 0; attachment->priv->loading = loading; attachment->priv->last_percent_notify = 0; @@ -595,12 +537,6 @@ attachment_set_loading (EAttachment *attachment, g_object_notify (G_OBJECT (attachment), "percent"); g_object_notify (G_OBJECT (attachment), "loading"); g_object_thaw_notify (G_OBJECT (attachment)); - - if (gtk_tree_row_reference_valid (reference)) { - GtkTreeModel *model; - model = gtk_tree_row_reference_get_model (reference); - g_object_notify (G_OBJECT (model), "num-loading"); - } } static void @@ -696,8 +632,8 @@ attachment_set_property (GObject *object, g_value_get_object (value)); return; - case PROP_SHOWN: - e_attachment_set_shown ( + case PROP_INITIALLY_SHOWN: + e_attachment_set_initially_shown ( E_ATTACHMENT (object), g_value_get_boolean (value)); return; @@ -708,12 +644,6 @@ attachment_set_property (GObject *object, g_value_get_object (value)); return; - case PROP_REFERENCE: - e_attachment_set_reference ( - E_ATTACHMENT (object), - g_value_get_boxed (value)); - return; - case PROP_SIGNED: e_attachment_set_signed ( E_ATTACHMENT (object), @@ -731,12 +661,6 @@ attachment_set_property (GObject *object, E_ATTACHMENT (object), g_value_get_boolean (value)); return; - - case PROP_ZOOM_TO_WINDOW: - e_attachment_set_zoom_to_window ( - E_ATTACHMENT (object), - g_value_get_boolean (value)); - return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -791,10 +715,10 @@ attachment_get_property (GObject *object, E_ATTACHMENT (object))); return; - case PROP_SHOWN: + case PROP_INITIALLY_SHOWN: g_value_set_boolean ( value, - e_attachment_get_shown ( + e_attachment_get_initially_shown ( E_ATTACHMENT (object))); return; @@ -819,13 +743,6 @@ attachment_get_property (GObject *object, E_ATTACHMENT (object))); return; - case PROP_REFERENCE: - g_value_set_boxed ( - value, - e_attachment_get_reference ( - E_ATTACHMENT (object))); - return; - case PROP_SAVE_SELF: g_value_set_boolean ( value, @@ -853,13 +770,6 @@ attachment_get_property (GObject *object, e_attachment_get_signed ( E_ATTACHMENT (object))); return; - - case PROP_ZOOM_TO_WINDOW: - g_value_set_boolean ( - value, - e_attachment_get_zoom_to_window ( - E_ATTACHMENT (object))); - return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -883,10 +793,6 @@ attachment_dispose (GObject *object) priv->emblem_timeout_id = 0; } - /* This accepts NULL arguments. */ - gtk_tree_row_reference_free (priv->reference); - priv->reference = NULL; - /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_attachment_parent_class)->dispose (object); } @@ -1030,16 +936,6 @@ e_attachment_class_init (EAttachmentClass *class) g_object_class_install_property ( object_class, - PROP_REFERENCE, - g_param_spec_boxed ( - "reference", - "Reference", - NULL, - GTK_TYPE_TREE_ROW_REFERENCE, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, PROP_SAVE_SELF, g_param_spec_boolean ( "save-self", @@ -1070,10 +966,10 @@ e_attachment_class_init (EAttachmentClass *class) g_object_class_install_property ( object_class, - PROP_SHOWN, + PROP_INITIALLY_SHOWN, g_param_spec_boolean ( - "shown", - "Shown", + "initially-shown", + "Initially Shown", NULL, FALSE, G_PARAM_READWRITE | @@ -1093,16 +989,46 @@ e_attachment_class_init (EAttachmentClass *class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property ( - object_class, - PROP_ZOOM_TO_WINDOW, - g_param_spec_boolean ( - "zoom-to-window", - "Zoom to window", - NULL, - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); + signals[UPDATE_FILE_INFO] = g_signal_new ( + "update-file-info", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAttachmentClass, update_file_info), + NULL, NULL, NULL, + G_TYPE_NONE, 4, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INT64); + + signals[UPDATE_ICON] = g_signal_new ( + "update-icon", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAttachmentClass, update_icon), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_ICON); + + signals[UPDATE_PROGRESS] = g_signal_new ( + "update-progress", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAttachmentClass, update_progress), + NULL, NULL, NULL, + G_TYPE_NONE, 3, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_INT); + + signals[LOAD_FAILED] = g_signal_new ( + "load-failed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAttachmentClass, load_failed), + NULL, NULL, NULL, + G_TYPE_NONE, 0, + G_TYPE_NONE); } static void @@ -1140,18 +1066,6 @@ e_attachment_init (EAttachment *attachment) attachment, "notify::percent", G_CALLBACK (attachment_update_progress_columns), NULL); - g_signal_connect ( - attachment, "notify::reference", - G_CALLBACK (attachment_update_file_info_columns), NULL); - - g_signal_connect ( - attachment, "notify::reference", - G_CALLBACK (attachment_update_icon_column), NULL); - - g_signal_connect ( - attachment, "notify::reference", - G_CALLBACK (attachment_update_progress_columns), NULL); - e_signal_connect_notify ( attachment, "notify::saving", G_CALLBACK (attachment_update_icon_column), NULL); @@ -1476,7 +1390,6 @@ void e_attachment_set_file_info (EAttachment *attachment, GFileInfo *file_info) { - GtkTreeRowReference *reference; GIcon *icon; g_return_if_fail (E_IS_ATTACHMENT (attachment)); @@ -1501,14 +1414,6 @@ e_attachment_set_file_info (EAttachment *attachment, g_mutex_unlock (&attachment->priv->property_lock); g_object_notify (G_OBJECT (attachment), "file-info"); - - /* Tell the EAttachmentStore its total size changed. */ - reference = e_attachment_get_reference (attachment); - if (gtk_tree_row_reference_valid (reference)) { - GtkTreeModel *model; - model = gtk_tree_row_reference_get_model (reference); - g_object_notify (G_OBJECT (model), "total-size"); - } } /** @@ -1616,29 +1521,6 @@ e_attachment_get_percent (EAttachment *attachment) return attachment->priv->percent; } -GtkTreeRowReference * -e_attachment_get_reference (EAttachment *attachment) -{ - g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); - - return attachment->priv->reference; -} - -void -e_attachment_set_reference (EAttachment *attachment, - GtkTreeRowReference *reference) -{ - g_return_if_fail (E_IS_ATTACHMENT (attachment)); - - if (reference != NULL) - reference = gtk_tree_row_reference_copy (reference); - - gtk_tree_row_reference_free (attachment->priv->reference); - attachment->priv->reference = reference; - - g_object_notify (G_OBJECT (attachment), "reference"); -} - gboolean e_attachment_get_saving (EAttachment *attachment) { @@ -1648,44 +1530,22 @@ e_attachment_get_saving (EAttachment *attachment) } gboolean -e_attachment_get_shown (EAttachment *attachment) -{ - g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); - - return attachment->priv->shown; -} - -void -e_attachment_set_shown (EAttachment *attachment, - gboolean shown) -{ - g_return_if_fail (E_IS_ATTACHMENT (attachment)); - - attachment->priv->shown = shown; - - g_object_notify (G_OBJECT (attachment), "shown"); -} - -gboolean -e_attachment_get_zoom_to_window (EAttachment *attachment) +e_attachment_get_initially_shown (EAttachment *attachment) { g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); - return attachment->priv->zoom_to_window; + return attachment->priv->initially_shown; } void -e_attachment_set_zoom_to_window (EAttachment *attachment, - gboolean zoom_to_window) +e_attachment_set_initially_shown (EAttachment *attachment, + gboolean initially_shown) { g_return_if_fail (E_IS_ATTACHMENT (attachment)); - if ((attachment->priv->zoom_to_window ? 1 : 0) == (zoom_to_window ? 1 : 0)) - return; - - attachment->priv->zoom_to_window = zoom_to_window; + attachment->priv->initially_shown = initially_shown; - g_object_notify (G_OBJECT (attachment), "zoom-to-window"); + g_object_notify (G_OBJECT (attachment), "initially-shown"); } gboolean @@ -1868,6 +1728,16 @@ exit: return app_info_list; } +void +e_attachment_update_store_columns (EAttachment *attachment) +{ + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + attachment_update_file_info_columns (attachment); + attachment_update_icon_column (attachment); + attachment_update_progress_columns (attachment); +} + /************************* e_attachment_load_async() *************************/ typedef struct _LoadContext LoadContext; @@ -2500,7 +2370,6 @@ e_attachment_load_handle_error (EAttachment *attachment, { GtkWidget *dialog; GFileInfo *file_info; - GtkTreeRowReference *reference; const gchar *display_name; const gchar *primary_text; GError *error = NULL; @@ -2512,17 +2381,7 @@ e_attachment_load_handle_error (EAttachment *attachment, if (e_attachment_load_finish (attachment, result, &error)) return; - /* XXX Calling EAttachmentStore functions from here violates - * the abstraction, but for now it's not hurting anything. */ - reference = e_attachment_get_reference (attachment); - if (gtk_tree_row_reference_valid (reference)) { - GtkTreeModel *model; - - model = gtk_tree_row_reference_get_model (reference); - - e_attachment_store_remove_attachment ( - E_ATTACHMENT_STORE (model), attachment); - } + g_signal_emit (attachment, signals[LOAD_FAILED], 0, NULL); /* Ignore cancellations. */ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { diff --git a/e-util/e-attachment.h b/e-util/e-attachment.h index d371008..5fa662e 100644 --- a/e-util/e-attachment.h +++ b/e-util/e-attachment.h @@ -60,6 +60,20 @@ struct _EAttachment { struct _EAttachmentClass { GObjectClass parent_class; + + /* Signals */ + void (*update_file_info) (EAttachment *attachment, + const gchar *caption, + const gchar *content_type, + const gchar *description, + gint64 size); + void (*update_icon) (EAttachment *attachment, + GIcon *icon); + void (*update_progress) (EAttachment *attachment, + gboolean loading, + gboolean saving, + gint percent); + void (*load_failed) (EAttachment *attachment); }; GType e_attachment_get_type (void) G_GNUC_CONST; @@ -92,17 +106,10 @@ CamelMimePart * e_attachment_ref_mime_part (EAttachment *attachment); void e_attachment_set_mime_part (EAttachment *attachment, CamelMimePart *mime_part); gint e_attachment_get_percent (EAttachment *attachment); -GtkTreeRowReference * - e_attachment_get_reference (EAttachment *attachment); -void e_attachment_set_reference (EAttachment *attachment, - GtkTreeRowReference *reference); gboolean e_attachment_get_saving (EAttachment *attachment); -gboolean e_attachment_get_shown (EAttachment *attachment); -void e_attachment_set_shown (EAttachment *attachment, - gboolean shown); -gboolean e_attachment_get_zoom_to_window (EAttachment *attachment); -void e_attachment_set_zoom_to_window (EAttachment *attachment, - gboolean zoom_to_window); +gboolean e_attachment_get_initially_shown(EAttachment *attachment); +void e_attachment_set_initially_shown(EAttachment *attachment, + gboolean initially_shown); gboolean e_attachment_get_save_self (EAttachment *attachment); void e_attachment_set_save_self (EAttachment *attachment, gboolean save_self); @@ -121,6 +128,8 @@ gchar * e_attachment_dup_description (EAttachment *attachment); gchar * e_attachment_dup_thumbnail_path (EAttachment *attachment); gboolean e_attachment_is_rfc822 (EAttachment *attachment); GList * e_attachment_list_apps (EAttachment *attachment); +void e_attachment_update_store_columns + (EAttachment *attachment); /* Asynchronous Operations */ void e_attachment_load_async (EAttachment *attachment, diff --git a/e-util/e-content-editor.c b/e-util/e-content-editor.c new file mode 100644 index 0000000..2224af0 --- /dev/null +++ b/e-util/e-content-editor.c @@ -0,0 +1,3583 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib-object.h> + +#include <libedataserver/libedataserver.h> + +#include "e-html-editor.h" +#include "e-util-enumtypes.h" +#include "e-content-editor.h" + +G_DEFINE_INTERFACE (EContentEditor, e_content_editor, GTK_TYPE_WIDGET); + +enum { + LOAD_FINISHED, + PASTE_CLIPBOARD, + PASTE_PRIMARY_CLIPBOARD, + CONTEXT_MENU_REQUESTED, + FIND_DONE, + REPLACE_ALL_DONE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +e_content_editor_default_init (EContentEditorInterface *iface) +{ + /** + * EContentEditor:can-copy + * + * Determines whether it's possible to copy to clipboard. The action + * is usually disabled when there is no selection to copy. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "can-copy", + "Can Copy", + NULL, + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:can-cut + * + * Determines whether it's possible to cut to clipboard. The action + * is usually disabled when there is no selection to cut. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "can-cut", + "Can Cut", + NULL, + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:can-paste + * + * Determines whether it's possible to paste from clipboard. The action + * is usually disabled when there is no valid content in clipboard to + * paste. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "can-paste", + "Can Paste", + NULL, + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:can-redo + * + * Determines whether it's possible to redo previous action. The action + * is usually disabled when there is no action to redo. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "can-redo", + "Can Redo", + NULL, + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:can-undo + * + * Determines whether it's possible to undo last action. The action + * is usually disabled when there is no previous action to undo. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "can-undo", + "Can Undo", + NULL, + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:editable + * + * Determines whether the editor is editable or read-only. + **/ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "editable", + "Editable", + "Wheter editor is editable", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:changed + * + * Determines whether document has been modified + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "changed", + "Changed property", + "Whether editor changed", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:html-mode + * + * Determines whether HTML or plain text mode is enabled. + **/ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "html-mode", + "HTML Mode", + "Edit HTML or plain text", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:alignment + * + * Holds alignment of current paragraph. + */ + g_object_interface_install_property ( + iface, + g_param_spec_enum ( + "alignment", + NULL, + NULL, + E_TYPE_CONTENT_EDITOR_ALIGNMENT, + E_CONTENT_EDITOR_ALIGNMENT_LEFT, + G_PARAM_READWRITE)); + + /** + * EContentEditor:background-color + * + * Holds background color of current selection or at current cursor + * position. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boxed ( + "background-color", + NULL, + NULL, + GDK_TYPE_RGBA, + G_PARAM_READWRITE)); + + /** + * EContentEditor:block-format + * + * Holds block format of current paragraph. See + * #EContentEditorBlockFormat for valid values. + */ + g_object_interface_install_property ( + iface, + g_param_spec_enum ( + "block-format", + NULL, + NULL, + E_TYPE_CONTENT_EDITOR_BLOCK_FORMAT, + E_CONTENT_EDITOR_BLOCK_FORMAT_NONE, + G_PARAM_READWRITE)); + + /** + * EContentEditor:bold + * + * Holds whether current selection or text at current cursor position + * is bold. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "bold", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:font-color + * + * Holds font color of current selection or at current cursor position. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boxed ( + "font-color", + NULL, + NULL, + GDK_TYPE_RGBA, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:font-name + * + * Holds name of font in current selection or at current cursor + * position. + */ + g_object_interface_install_property ( + iface, + g_param_spec_string ( + "font-name", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:font-size + * + * Holds point size of current selection or at current cursor position. + */ + g_object_interface_install_property ( + iface, + g_param_spec_int ( + "font-size", + NULL, + NULL, + 1, + 7, + 3, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:indented + * + * Holds whether current paragraph is indented. This does not include + * citations. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "indented", + NULL, + NULL, + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:italic + * + * Holds whether current selection or letter at current cursor position + * is italic. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "italic", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:monospaced + * + * Holds whether current selection or letter at current cursor position + * is monospaced. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "monospaced", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:strikethrough + * + * Holds whether current selection or letter at current cursor position + * is strikethrough. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "strikethrough", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:superscript + * + * Holds whether current selection or letter at current cursor position + * is in superscript. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "superscript", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:subscript + * + * Holds whether current selection or letter at current cursor position + * is in subscript. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "subscript", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:underline + * + * Holds whether current selection or letter at current cursor position + * is underlined. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "underline", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:spell-check-enabled + * + * Holds whether the spell checking is enabled. + */ + g_object_interface_install_property ( + iface, + g_param_spec_boolean ( + "spell-check-enabled", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:spell-checker: + * + * The #ESpellChecker used for spell checking. + **/ + g_object_interface_install_property ( + iface, + g_param_spec_object ( + "spell-checker", + "Spell Checker", + "The spell checker", + E_TYPE_SPELL_CHECKER, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * EContentEditor:paste-clipboard + * + * Emitted when user presses middle button on EContentEditor. + */ + signals[PASTE_CLIPBOARD] = g_signal_new ( + "paste-clipboard", + E_TYPE_CONTENT_EDITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EContentEditorInterface, paste_clipboard), + g_signal_accumulator_true_handled, NULL, + NULL, + G_TYPE_BOOLEAN, 0); + + /** + * EContentEditor:paste-primary-clipboard + * + * Emitted when user presses middle button on EWebKitContentEditor. + */ + signals[PASTE_PRIMARY_CLIPBOARD] = g_signal_new ( + "paste-primary-clipboard", + E_TYPE_CONTENT_EDITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EContentEditorInterface, paste_primary_clipboard), + g_signal_accumulator_true_handled, NULL, + NULL, + G_TYPE_BOOLEAN, 0); + + /** + * EContentEditor:load-finished + * + * Emitted when the content editor has finished loading. + */ + signals[LOAD_FINISHED] = g_signal_new ( + "load-finished", + E_TYPE_CONTENT_EDITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EContentEditorInterface, load_finished), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * EContentEditor:context-menu-requested + * + * Emitted whenever a context menu is requested. + */ + signals[CONTEXT_MENU_REQUESTED] = g_signal_new ( + "context-menu-requested", + E_TYPE_CONTENT_EDITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EContentEditorInterface, context_menu_requested), + g_signal_accumulator_true_handled, NULL, + NULL, + G_TYPE_BOOLEAN, 2, + G_TYPE_INT, + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + + /** + * EContentEditor::find-done + * + * Emitted when the call to e_content_editor_find() is done. + **/ + signals[FIND_DONE] = g_signal_new ( + "find-done", + E_TYPE_CONTENT_EDITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EContentEditorInterface, find_done), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + G_TYPE_UINT); + + /** + * EContentEditor::replace-all-done + * + * Emitted when the call to e_content_editor_replace_all() is done. + **/ + signals[REPLACE_ALL_DONE] = g_signal_new ( + "replace-all-done", + E_TYPE_CONTENT_EDITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EContentEditorInterface, replace_all_done), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + G_TYPE_UINT); +} + +ESpellChecker * +e_content_editor_ref_spell_checker (EContentEditor *editor) +{ + ESpellChecker *spell_checker = NULL; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + g_object_get (G_OBJECT (editor), "spell-checker", &spell_checker, NULL); + + return spell_checker; +} + +gboolean +e_content_editor_can_cut (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "can-cut", &value, NULL); + + return value; +} + +gboolean +e_content_editor_can_copy (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "can-copy", &value, NULL); + + return value; +} + +gboolean +e_content_editor_can_paste (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "can-paste", &value, NULL); + + return value; +} + +gboolean +e_content_editor_can_undo (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "can-undo", &value, NULL); + + return value; +} + +gboolean +e_content_editor_can_redo (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "can-redo", &value, NULL); + + return value; +} + +/** + * e_content_editor_is_indented: + * @editor: an #EContentEditor + * + * Returns whether the current paragraph is indented. This does not include + * citations. + * + * Returns: %TRUE when current paragraph is indented, %FALSE otherwise. + * + * Since: 3.22 + **/ +gboolean +e_content_editor_is_indented (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "indented", &value, NULL); + + return value; +} + +gboolean +e_content_editor_get_spell_check_enabled (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "spell-check-enabled", &value, NULL); + + return value; +} + +void +e_content_editor_set_spell_check_enabled (EContentEditor *editor, + gboolean enable) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "spell-check-enabled", enable, NULL); +} + +gboolean +e_content_editor_is_editable (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "editable", &value, NULL); + + return value; +} + +void +e_content_editor_set_editable (EContentEditor *editor, + gboolean editable) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "editable", editable, NULL); +} + +gboolean +e_content_editor_get_changed (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "changed", &value, NULL); + + return value; +} + +void +e_content_editor_set_changed (EContentEditor *editor, + gboolean changed) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "changed", changed, NULL); +} + +gboolean +e_content_editor_get_html_mode (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "html-mode", &value, NULL); + + return value; +} + +void +e_content_editor_set_html_mode (EContentEditor *editor, + gboolean html_mode) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "html-mode", html_mode, NULL); +} + +/** + * e_content_editor_set_alignment: + * @editor: an #EContentEditor + * @value: an #EContentEditorAlignment value to apply + * + * Sets alignment of current paragraph to @value. + * + * Since: 3.22 + **/ +void +e_content_editor_set_alignment (EContentEditor *editor, + EContentEditorAlignment value) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "alignment", value, NULL); +} + +/** + * e_content_editor_get_alignment: + * @editor: #an EContentEditor + * + * Returns alignment of the current paragraph. + * + * Returns: #EContentEditorAlignment + * + * Since: 3.22 + **/ +EContentEditorAlignment +e_content_editor_get_alignment (EContentEditor *editor) +{ + EContentEditorAlignment value = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), E_CONTENT_EDITOR_ALIGNMENT_LEFT); + + g_object_get (G_OBJECT (editor), "alignment", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_background_color: + * @editor: an #EContentEditor + * @value: a #GdkRGBA + * + * Sets the background color of the current selection or letter at the current cursor position to + * a color defined by @value. + * + * Since: 3.22 + **/ +void +e_content_editor_set_background_color (EContentEditor *editor, + const GdkRGBA *value) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + g_object_set (G_OBJECT (editor), "background-color", value, NULL); +} + +/** + * e_content_editor_dup_background_color: + * @editor: an #EContentEditor + * + * Returns the background color used in the current selection or at letter + * at the current cursor position. + * + * Returns: (transfer-full): A newly allocated #GdkRGBA structure with + * the current background color. Free the returned value with gdk_rgba_free() + * when done with it. + * + * Since: 3.22 + **/ +GdkRGBA * +e_content_editor_dup_background_color (EContentEditor *editor) +{ + GdkRGBA *value = NULL; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + g_object_get (G_OBJECT (editor), "background-color", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_font_color: + * @editor: an #EContentEditor + * @value: a #GdkRGBA + * + * Sets the font color of the current selection or letter at the current cursor position to + * a color defined by @value. + * + * Since: 3.22 + **/ +void +e_content_editor_set_font_color (EContentEditor *editor, + const GdkRGBA *value) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + g_object_set (G_OBJECT (editor), "font-color", value, NULL); +} + +/** + * e_content_editor_dup_font_color: + * @editor: an #EContentEditor + * + * Returns the font color used in the current selection or at letter + * at the current cursor position. + * + * Returns: (transfer-full): A newly allocated #GdkRGBA structure with + * the current font color. Free the returned value with gdk_rgba_free() + * when done with it. + * + * Since: 3.22 + **/ +GdkRGBA * +e_content_editor_dup_font_color (EContentEditor *editor) +{ + GdkRGBA *value = NULL; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + g_object_get (G_OBJECT (editor), "font-color", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_font_name: + * @editor: an #EContentEditor + * @value: a font name to apply + * + * Sets font name of current selection or of letter at current cursor position + * to @value. + * + * Since: 3.22 + **/ +void +e_content_editor_set_font_name (EContentEditor *editor, + const gchar *value) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + g_object_set (G_OBJECT (editor), "font-name", value, NULL); +} + +/** + * e_content_editor_dup_font_name: + * @editor: an #EContentEditor + * + * Returns a name of the font used in the current selection or at letter + * at the current cursor position. + * + * Returns: (transfer-full): A newly allocated string with the font name. + * Free it with g_free() when done with it. + * + * Since: 3.22 + **/ +gchar * +e_content_editor_dup_font_name (EContentEditor *editor) +{ + gchar *value = NULL; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + g_object_get (G_OBJECT (editor), "font-name", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_font_size: + * @editor: an #EContentEditor + * @value: font size to apply + * + * Sets font size of current selection or of letter at current cursor position + * to @value. + * + * Since: 3.22 + **/ +void +e_content_editor_set_font_size (EContentEditor *editor, + gint value) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "font-size", value, NULL); +} + +/** + * e_content_editor_get_font_size: + * @editor: an #EContentEditor + * + * Returns fotn size of the current selection or letter at the current + * cursor position. + * + * Returns: Current font size. + * + * Since: 3.22 + **/ +gint +e_content_editor_get_font_size (EContentEditor *editor) +{ + gint value = -1; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), -1); + + g_object_get (G_OBJECT (editor), "font-size", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_block_format: + * @editor: an #EContentEditor + * @value: an #EContentEditorBlockFormat value + * + * Changes block format of the current paragraph to @value. + * + * Since: 3.22 + **/ +void +e_content_editor_set_block_format (EContentEditor *editor, + EContentEditorBlockFormat value) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "block-format", value, NULL); +} + +/** + * e_content_editor_get_block_format: + * @editor: an #EContentEditor + * + * Returns block format of the current paragraph. + * + * Returns: #EContentEditorBlockFormat + * + * Since: 3.22 + **/ +EContentEditorBlockFormat +e_content_editor_get_block_format (EContentEditor *editor) +{ + EContentEditorBlockFormat value = E_CONTENT_EDITOR_BLOCK_FORMAT_NONE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), E_CONTENT_EDITOR_BLOCK_FORMAT_NONE); + + g_object_get (G_OBJECT (editor), "block-format", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_bold: + * @editor: an #EContentEditor + * @bold: %TRUE to enable bold, %FALSE to disable + * + * Changes bold formatting of current selection or letter at current + * cursor position. + * + * Since: 3.22 + **/ +void +e_content_editor_set_bold (EContentEditor *editor, + gboolean bold) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "bold", bold, NULL); +} + +/** + * e_content_editor_is_bold: + * @editor: an #EContentEditor + * + * Returns whether current selection or letter at current cursor position is bold. + * + * Returns: %TRUE when selection is bold, %FALSE otherwise. + * + * Since: 3.22 + **/ +gboolean +e_content_editor_is_bold (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "bold", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_italic: + * @editor: an #EContentEditor + * @italic: %TRUE to enable italic, %FALSE to disable + * + * Changes italic formatting of current selection or letter at current + * cursor position. + * + * Since: 3.22 + **/ +void +e_content_editor_set_italic (EContentEditor *editor, + gboolean italic) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "italic", italic, NULL); +} + +/** + * e_content_editor_is_italic: + * @editor: an #EContentEditor + * + * Returns whether current selection or letter at current cursor position + * is italic. + * + * Returns: %TRUE when selection is italic, %FALSE otherwise. + * + * Since: 3.22 + **/ +gboolean +e_content_editor_is_italic (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "italic", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_monospaced: + * @editor: an #EContentEditor + * @monospaced: %TRUE to enable monospaced, %FALSE to disable + * + * Changes monospaced formatting of current selection or letter + * at current cursor position. + * + * Since: 3.22 + **/ +void +e_content_editor_set_monospaced (EContentEditor *editor, + gboolean monospaced) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "monospaced", monospaced, NULL); +} + +/** + * e_content_editor_is_monospaced: + * @editor: an #EContentEditor + * + * Returns whether current selection or letter at current cursor position + * is monospaced. + * + * Returns: %TRUE when selection is monospaced, %FALSE otherwise. + * + * Since: 3.22 + **/ +gboolean +e_content_editor_is_monospaced (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "monospaced", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_strikethrough: + * @editor: an #EContentEditor + * @strikethrough: %TRUE to enable strikethrough, %FALSE to disable + * + * Changes strike through formatting of current selection or letter at current + * cursor position. + * + * Since: 3.22 + **/ +void +e_content_editor_set_strikethrough (EContentEditor *editor, + gboolean strikethrough) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "strikethrough", strikethrough, NULL); +} + +/** + * e_content_editor_is_strikethrough: + * @editor: an #EContentEditor + * + * Returns whether current selection or letter at current cursor position + * is striked through. + * + * Returns: %TRUE when selection is striked through, %FALSE otherwise. + * + * Since: 3.22 + **/ +gboolean +e_content_editor_is_strikethrough (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "strikethrough", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_subscript: + * @editor: an #EContentEditor + * @subscript: %TRUE to enable subscript, %FALSE to disable + * + * Changes subscript of current selection or letter at current + * cursor position. + * + * Since: 3.22 + **/ +void +e_content_editor_set_subscript (EContentEditor *editor, + gboolean subscript) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "subscript", subscript, NULL); +} + +/** + * e_content_editor_is_subscript: + * @editor: an #EContentEditor + * + * Returns whether current selection or letter at current cursor position + * is in subscript. + * + * Returns: %TRUE when selection is in subscript, %FALSE otherwise. + * + * Since: 3.22 + **/ +gboolean +e_content_editor_is_subscript (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "subscript", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_superscript: + * @editor: an #EContentEditor + * @superscript: %TRUE to enable superscript, %FALSE to disable + * + * Changes superscript of the current selection or letter at current + * cursor position. + * + * Since: 3.22 + **/ +void +e_content_editor_set_superscript (EContentEditor *editor, + gboolean superscript) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "superscript", superscript, NULL); +} + +/** + * e_content_editor_is_superscript: + * @editor: an #EContentEditor + * + * Returns whether current selection or letter at current cursor position + * is in superscript. + * + * Returns: %TRUE when selection is in superscript, %FALSE otherwise. + * + * Since: 3.22 + **/ +gboolean +e_content_editor_is_superscript (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "superscript", &value, NULL); + + return value; +} + +/** + * e_content_editor_set_underline: + * @editor: an #EContentEditor + * @underline: %TRUE to enable underline, %FALSE to disable + * + * Changes underline formatting of current selection or letter + * at current cursor position. + * + * Since: 3.22 + **/ +void +e_content_editor_set_underline (EContentEditor *editor, + gboolean underline) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_object_set (G_OBJECT (editor), "underline", underline, NULL); +} + +/** + * e_content_editor_is_underline: + * @editor: an #EContentEditor + * + * Returns whether current selection or letter at current cursor position + * is underlined. + * + * Returns: %TRUE when selection is underlined, %FALSE otherwise. + * + * Since: 3.22 + **/ +gboolean +e_content_editor_is_underline (EContentEditor *editor) +{ + gboolean value = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_object_get (G_OBJECT (editor), "underline", &value, NULL); + + return value; +} + +/** + * e_content_editor_setup_editor: + * @content_editor: an #EContentEditor + * @callback: an #EContentEditorInitializedCallback function + * @user_data: data to pass to @callback + * + * Initilizes the @content_editor. Once the initialization is done, + * the @callback is called with the passed @user_data. + * + * Since: 3.22 + **/ +void +e_content_editor_initialize (EContentEditor *content_editor, + EContentEditorInitializedCallback callback, + gpointer user_data) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (content_editor)); + g_return_if_fail (callback != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (content_editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->initialize != NULL); + + iface->initialize (content_editor, callback, user_data); +} + +/** + * e_content_editor_setup_editor: + * @content_editor: an #EContentEditor + * @html_editor: an #EHTMLEditor + * + * Called the first time the @content_editor is picked to be used within + * the @html_editor. This is typically used to modify the UI + * of the @html_editor. This method implementation is optional. + * + * Since: 3.22 + **/ +void +e_content_editor_setup_editor (EContentEditor *content_editor, + EHTMLEditor *html_editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (content_editor)); + g_return_if_fail (E_IS_HTML_EDITOR (html_editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (content_editor); + g_return_if_fail (iface != NULL); + + if (iface->setup_editor) + iface->setup_editor (content_editor, html_editor); +} + +void +e_content_editor_update_styles (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->update_styles != NULL); + + iface->update_styles (editor); +} + +void +e_content_editor_insert_content (EContentEditor *editor, + const gchar *content, + EContentEditorInsertContentFlags flags) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (content != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->insert_content != NULL); + + iface->insert_content (editor, content, flags); +} + +gchar * +e_content_editor_get_content (EContentEditor *editor, + EContentEditorGetContentFlags flags, + const gchar *inline_images_from_domain, + GSList **inline_images_parts /* newly created CamelMimePart * */) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + if ((flags & E_CONTENT_EDITOR_GET_INLINE_IMAGES)) { + g_return_val_if_fail (inline_images_from_domain != NULL, NULL); + g_return_val_if_fail (inline_images_parts != NULL, NULL); + } + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->get_content != NULL, NULL); + + return iface->get_content (editor, flags, inline_images_from_domain, inline_images_parts); +} + +void +e_content_editor_insert_image_from_mime_part (EContentEditor *editor, + CamelMimePart *part) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (part != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->insert_image_from_mime_part != NULL); + + iface->insert_image_from_mime_part (editor, part); +} + +/** + * e_content_editor_insert_image: + * @editor: an #EContentEditor + * @uri: an URI of the source image + * + * Inserts image at current cursor position using @uri as source. When a + * text range is selected, it will be replaced by the image. + * + * Since: 3.22 + **/ +void +e_content_editor_insert_image (EContentEditor *editor, + const gchar *uri) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (uri != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->insert_image != NULL); + + iface->insert_image (editor, uri); +} + +void +e_content_editor_insert_emoticon (EContentEditor *editor, + EEmoticon *emoticon) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (emoticon != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->insert_emoticon != NULL); + + iface->insert_emoticon (editor, emoticon); +} + +void +e_content_editor_move_caret_on_coordinates (EContentEditor *editor, + gint x, + gint y, + gboolean cancel_if_not_collapsed) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (x > 0); + g_return_if_fail (y > 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->move_caret_on_coordinates != NULL); + + iface->move_caret_on_coordinates (editor, x, y, cancel_if_not_collapsed); +} + +void +e_content_editor_cut (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cut != NULL); + + iface->cut (editor); +} + +void +e_content_editor_copy (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->copy != NULL); + + iface->copy (editor); +} + +void +e_content_editor_paste (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->paste != NULL); + + iface->paste (editor); +} + +void +e_content_editor_paste_primary (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->paste_primary != NULL); + + iface->paste_primary (editor); +} + +void +e_content_editor_undo (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->undo != NULL); + + iface->undo (editor); +} + +void +e_content_editor_redo (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->redo != NULL); + + iface->redo (editor); +} + +void +e_content_editor_clear_undo_redo_history (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->clear_undo_redo_history != NULL); + + iface->clear_undo_redo_history (editor); +} + +void +e_content_editor_set_spell_checking_languages (EContentEditor *editor, + const gchar **languages) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->set_spell_checking_languages != NULL); + + iface->set_spell_checking_languages (editor, languages); +} + +void +e_content_editor_select_all (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->select_all != NULL); + + iface->select_all (editor); +} + +/** + * e_content_editor_get_selected_text: + * @editor: an #EContentEditor + * + * Returns currently selected string. + * + * Returns: (transfer-full): A newly allocated string with the content of current selection. + * + * Since: 3.22 + **/ +gchar * +e_content_editor_get_selected_text (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->get_selected_text != NULL, NULL); + + return iface->get_selected_text (editor); +} + +/** + * e_content_editor_get_caret_word: + * @editor: an #EContentEditor + * + * Returns word under cursor. + * + * Returns: (transfer-full): A newly allocated string with current caret word or %NULL + * when there is no text under cursor or when selection is active. + * + * Since: 3.22 + **/ +gchar * +e_content_editor_get_caret_word (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->get_caret_word != NULL, NULL); + + return iface->get_caret_word (editor); +} + +/** + * e_content_editor_replace_caret_word: + * @editor: an #EContentEditor + * @replacement: a string to replace current caret word with + * + * Replaces current word under cursor with @replacement. + * + * Since: 3.22 + **/ +void +e_content_editor_replace_caret_word (EContentEditor *editor, + const gchar *replacement) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (replacement != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->replace_caret_word != NULL); + + iface->replace_caret_word (editor, replacement); +} + +void +e_content_editor_selection_indent (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->selection_indent != NULL); + + iface->selection_indent (editor); +} + +void +e_content_editor_selection_unindent (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->selection_unindent != NULL); + + iface->selection_unindent (editor); +} + +/** + * e_content_editor_selection_create_link: + * @editor: an #EContentEditor + * @uri: destination of the new link + * + * Converts current selection into a link pointing to @url. + * + * Since: 3.22 + **/ +void +e_content_editor_selection_create_link (EContentEditor *editor, + const gchar *uri) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (uri != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->selection_create_link != NULL); + + iface->selection_create_link (editor, uri); +} + +/** + * e_content_editor_selection_unlink: + * @editor: an #EContentEditor + * + * Removes any links (<A> elements) from current selection or at current + * cursor position. + * + * Since: 3.22 + **/ +void +e_content_editor_selection_unlink (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->selection_unlink != NULL); + + iface->selection_unlink (editor); +} + +/** + * e_content_editor_find: + * @editor: an #EContentEditor + * @flags: a bit-OR of #EContentEditorFindFlags flags + * @text: a text to find + * + * Searches the content of the @editor for the occurrence of the @text. + * The @flags modify the behaviour of the search. The found text, + * if any, is supposed to be selected. + * + * Once the search is done, the "find-done" signal should be + * emitted, by using e_content_editor_emit_find_done(). + * + * Since: 3.22 + **/ +void +e_content_editor_find (EContentEditor *editor, + guint32 flags, + const gchar *text) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (text != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->find != NULL); + + iface->find (editor, flags, text); +} + +/** + * e_content_editor_replace: + * @editor: an #EContentEditor + * @replacement: a string to replace current selection with + * + * Replaces currently selected text with @replacement. + * + * Since: 3.22 + **/ +void +e_content_editor_replace (EContentEditor *editor, + const gchar *replacement) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (replacement != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->replace != NULL); + + iface->replace (editor, replacement); +} + +/** + * e_content_editor_replace_all: + * @editor: an #EContentEditor + * @flags: a bit-OR of #EContentEditorFindFlags flags + * @find_text: a text to find + * @replace_with: a text to replace the found text with + * + * Searches the content of the @editor for all the occurrences of + * the @find_text and replaces them with the @replace_with. + * The @flags modify the behaviour of the search. + * + * Once the replace is done, the "replace-all-done" signal should be + * emitted, by using e_content_editor_emit_replace_all_done(). + * + * Since: 3.22 + **/ +void +e_content_editor_replace_all (EContentEditor *editor, + guint32 flags, + const gchar *find_text, + const gchar *replace_with) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (find_text != NULL); + g_return_if_fail (replace_with != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->replace_all != NULL); + + iface->replace_all (editor, flags, find_text, replace_with); +} + +/** + * e_content_editor_selection_save: + * @editor: an #EContentEditor + * + * Saves current cursor position or current selection range. The selection can + * be later restored by calling e_content_editor_selection_restore(). + * + * Note that calling e_content_editor_selection_save() overwrites previously saved + * position. + * + * Note that this method inserts special markings into the HTML code that are + * used to later restore the selection. It can happen that by deleting some + * segments of the document some of the markings are deleted too. In that case + * restoring the selection by e_content_editor_selection_restore() can fail. Also by + * moving text segments (Cut & Paste) can result in moving the markings + * elsewhere, thus e_content_editor_selection_restore() will restore the selection + * incorrectly. + * + * It is recommended to use this method only when you are not planning to make + * bigger changes to content or structure of the document (formatting changes + * are usually OK). + * + * Since: 3.22 + **/ +void +e_content_editor_selection_save (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->selection_save != NULL); + + iface->selection_save (editor); +} + +/** + * e_content_editor_selection_restore: + * @editor: an #EContentEditor + * + * Restores cursor position or selection range that was saved by + * e_content_editor_selection_save(). + * + * Note that calling this function without calling e_content_editor_selection_save() + * before is a programming error and the behavior is undefined. + * + * Since: 3.22 + **/ +void +e_content_editor_selection_restore (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->selection_restore != NULL); + + iface->selection_restore (editor); +} + +void +e_content_editor_selection_wrap (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->selection_wrap != NULL); + + iface->selection_wrap (editor); +} + +guint +e_content_editor_get_caret_position (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->get_caret_position != NULL, 0); + + return iface->get_caret_position (editor); +} + +guint +e_content_editor_get_caret_offset (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->get_caret_offset != NULL, 0); + + return iface->get_caret_offset (editor); +} + +gchar * +e_content_editor_get_current_signature_uid (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->get_current_signature_uid != NULL, NULL); + + return iface->get_current_signature_uid (editor); +} + +gboolean +e_content_editor_is_ready (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->is_ready != NULL, FALSE); + + return iface->is_ready (editor); +} + +gchar * +e_content_editor_insert_signature (EContentEditor *editor, + const gchar *content, + gboolean is_html, + const gchar *signature_id, + gboolean *set_signature_from_message, + gboolean *check_if_signature_is_changed, + gboolean *ignore_next_signature_change) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->insert_signature != NULL, FALSE); + + return iface->insert_signature ( + editor, + content, + is_html, + signature_id, + set_signature_from_message, + check_if_signature_is_changed, + ignore_next_signature_change); +} + +void +e_content_editor_delete_cell_contents (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->delete_cell_contents != NULL); + + iface->delete_cell_contents (editor); +} + +void +e_content_editor_delete_column (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->delete_column != NULL); + + iface->delete_column (editor); +} + +void +e_content_editor_delete_row (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->delete_row != NULL); + + iface->delete_row (editor); +} + +void +e_content_editor_delete_table (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->delete_table != NULL); + + iface->delete_table (editor); +} + +void +e_content_editor_insert_column_after (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->insert_column_after != NULL); + + iface->insert_column_after (editor); +} + +void +e_content_editor_insert_column_before (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->insert_column_before != NULL); + + iface->insert_column_before (editor); +} + +void +e_content_editor_insert_row_above (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->insert_row_above != NULL); + + iface->insert_row_above (editor); +} + +void +e_content_editor_insert_row_below (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->insert_row_below != NULL); + + iface->insert_row_below (editor); +} + +gboolean +e_content_editor_on_h_rule_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->on_h_rule_dialog_open != NULL, FALSE); + + return iface->on_h_rule_dialog_open (editor); +} + +void +e_content_editor_on_h_rule_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_h_rule_dialog_close != NULL); + + iface->on_h_rule_dialog_close (editor); +} + +void +e_content_editor_h_rule_set_align (EContentEditor *editor, + const gchar *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->h_rule_set_align != NULL); + + iface->h_rule_set_align (editor, value); +} + +gchar * +e_content_editor_h_rule_get_align (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->h_rule_get_align != NULL, NULL); + + return iface->h_rule_get_align (editor); +} + +void +e_content_editor_h_rule_set_size (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->h_rule_set_size != NULL); + + iface->h_rule_set_size (editor, value); +} + +gint +e_content_editor_h_rule_get_size (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->h_rule_get_size != NULL, 0); + + return iface->h_rule_get_size (editor); +} + +void +e_content_editor_h_rule_set_width (EContentEditor *editor, + gint value, + EContentEditorUnit unit) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->h_rule_set_width != NULL); + + iface->h_rule_set_width (editor, value, unit); +} + +gint +e_content_editor_h_rule_get_width (EContentEditor *editor, + EContentEditorUnit *unit) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + g_return_val_if_fail (unit != NULL, 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->h_rule_get_width != NULL, 0); + + return iface->h_rule_get_width (editor, unit); +} + +void +e_content_editor_h_rule_set_no_shade (EContentEditor *editor, + gboolean value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->h_rule_set_no_shade != NULL); + + iface->h_rule_set_no_shade (editor, value); +} + +gboolean +e_content_editor_h_rule_get_no_shade (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->h_rule_get_no_shade != NULL, FALSE); + + return iface->h_rule_get_no_shade (editor); +} + +void +e_content_editor_on_image_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_image_dialog_open != NULL); + + iface->on_image_dialog_open (editor); +} + +void +e_content_editor_on_image_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_image_dialog_close != NULL); + + iface->on_image_dialog_close (editor); +} + +void +e_content_editor_image_set_width_follow (EContentEditor *editor, + gboolean value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_width_follow != NULL); + + iface->image_set_width_follow (editor, value); +} + +void +e_content_editor_image_set_src (EContentEditor *editor, + const gchar *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_src != NULL); + + iface->image_set_src (editor, value); +} + +gchar * +e_content_editor_image_get_src (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->image_get_src != NULL, FALSE); + + return iface->image_get_src (editor); +} + +void +e_content_editor_image_set_alt (EContentEditor *editor, + const gchar *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_alt != NULL); + + iface->image_set_alt (editor, value); +} + +gchar * +e_content_editor_image_get_alt (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->image_get_alt != NULL, FALSE); + + return iface->image_get_alt (editor); +} + +void +e_content_editor_image_set_url (EContentEditor *editor, + const gchar *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_url != NULL); + + iface->image_set_url (editor, value); +} + +gchar * +e_content_editor_image_get_url (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->image_get_url != NULL, FALSE); + + return iface->image_get_url (editor); +} + +void +e_content_editor_image_set_vspace (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_vspace != NULL); + + iface->image_set_vspace (editor, value); +} + +gint +e_content_editor_image_get_vspace (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->image_get_vspace != NULL, 0); + + return iface->image_get_vspace (editor); +} + + +void +e_content_editor_image_set_hspace (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_hspace != NULL); + + iface->image_set_hspace (editor, value); +} + +gint +e_content_editor_image_get_hspace (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->image_get_hspace != NULL, 0); + + return iface->image_get_hspace (editor); +} + +void +e_content_editor_image_set_border (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_border != NULL); + + iface->image_set_border (editor, value); +} + +gint +e_content_editor_image_get_border (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->image_get_border != NULL, FALSE); + + return iface->image_get_border (editor); +} + +void +e_content_editor_image_set_align (EContentEditor *editor, + const gchar *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_align != NULL); + + iface->image_set_align (editor, value); +} + +gchar * +e_content_editor_image_get_align (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->image_get_align != NULL, FALSE); + + return iface->image_get_align (editor); +} + +gint32 +e_content_editor_image_get_natural_width (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->image_get_natural_width != NULL, 0); + + return iface->image_get_natural_width (editor); +} + +gint32 +e_content_editor_image_get_natural_height (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->image_get_natural_height != NULL, 0); + + return iface->image_get_natural_height (editor); +} + +void +e_content_editor_image_set_width (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_width != NULL); + + iface->image_set_width (editor, value); +} + +gint32 +e_content_editor_image_get_width (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->image_get_width != NULL, 0); + + return iface->image_get_width (editor); +} + +void +e_content_editor_image_set_height (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_height != NULL); + + iface->image_set_height (editor, value); +} + +gint32 +e_content_editor_image_get_height (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->image_get_height != NULL, 0); + + return iface->image_get_height (editor); +} + +void +e_content_editor_image_set_height_follow (EContentEditor *editor, + gboolean value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->image_set_height_follow != NULL); + + iface->image_set_height_follow (editor, value); +} + +void +e_content_editor_on_link_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_link_dialog_open != NULL); + + iface->on_link_dialog_open (editor); +} + +void +e_content_editor_on_link_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_link_dialog_close != NULL); + + iface->on_link_dialog_close (editor); +} + +void +e_content_editor_link_get_values (EContentEditor *editor, + gchar **href, + gchar **text) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->link_get_values != NULL); + + return iface->link_get_values (editor, href, text); +} + +void +e_content_editor_link_set_values (EContentEditor *editor, + const gchar *href, + const gchar *text) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->link_set_values != NULL); + + iface->link_set_values (editor, href, text); +} + +void +e_content_editor_on_page_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_page_dialog_open != NULL); + + iface->on_page_dialog_open (editor); +} + +void +e_content_editor_on_page_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_page_dialog_close != NULL); + + iface->on_page_dialog_close (editor); +} + +void +e_content_editor_page_set_text_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_set_text_color != NULL); + + iface->page_set_text_color (editor, value); +} + +void +e_content_editor_page_get_text_color (EContentEditor *editor, + GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_get_text_color != NULL); + + return iface->page_get_text_color (editor, value); +} + +void +e_content_editor_page_set_background_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_set_background_color != NULL); + + iface->page_set_background_color (editor, value); +} + +void +e_content_editor_page_get_background_color (EContentEditor *editor, + GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_get_background_color != NULL); + + return iface->page_get_background_color (editor, value); +} + +void +e_content_editor_page_set_link_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_set_link_color != NULL); + + iface->page_set_link_color (editor, value); +} + +void +e_content_editor_page_get_link_color (EContentEditor *editor, + GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_get_link_color != NULL); + + return iface->page_get_link_color (editor, value); +} + +void +e_content_editor_page_set_visited_link_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_set_visited_link_color != NULL); + + iface->page_set_visited_link_color (editor, value); +} + +void +e_content_editor_page_get_visited_link_color (EContentEditor *editor, + GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_get_visited_link_color != NULL); + + return iface->page_get_visited_link_color (editor, value); +} + +/* uri could be NULL -> removes the current image */ +void +e_content_editor_page_set_background_image_uri (EContentEditor *editor, + const gchar *uri) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->page_set_background_image_uri != NULL); + + iface->page_set_background_image_uri (editor, uri); +} + +gchar * +e_content_editor_page_get_background_image_uri (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->page_get_background_image_uri != NULL, NULL); + + return iface->page_get_background_image_uri (editor); +} + +void +e_content_editor_on_cell_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_cell_dialog_open != NULL); + + iface->on_cell_dialog_open (editor); +} + +void +e_content_editor_on_cell_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_cell_dialog_close != NULL); + + iface->on_cell_dialog_close (editor); +} + +void +e_content_editor_cell_set_v_align (EContentEditor *editor, + const gchar *value, + EContentEditorScope scope) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_v_align != NULL); + + iface->cell_set_v_align (editor, value, scope); +} + +gchar * +e_content_editor_cell_get_v_align (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->cell_get_v_align != NULL, NULL); + + return iface->cell_get_v_align (editor); +} + +void +e_content_editor_cell_set_align (EContentEditor *editor, + const gchar *value, + EContentEditorScope scope) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_align != NULL); + + iface->cell_set_align (editor, value, scope); +} + +gchar * +e_content_editor_cell_get_align (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->cell_get_align != NULL, NULL); + + return iface->cell_get_align (editor); +} + +void +e_content_editor_cell_set_wrap (EContentEditor *editor, + gboolean value, + EContentEditorScope scope) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_wrap != NULL); + + iface->cell_set_wrap (editor, value, scope); +} + +gboolean +e_content_editor_cell_get_wrap (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->cell_get_wrap != NULL, FALSE); + + return iface->cell_get_wrap (editor); +} + +void +e_content_editor_cell_set_header_style (EContentEditor *editor, + gboolean value, + EContentEditorScope scope) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_header_style != NULL); + + iface->cell_set_header_style (editor, value, scope); +} + +gboolean +e_content_editor_cell_is_header (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->cell_is_header != NULL, FALSE); + + return iface->cell_is_header (editor); +} + +void +e_content_editor_cell_set_width (EContentEditor *editor, + gint value, + EContentEditorUnit unit, + EContentEditorScope scope) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_width != NULL); + + iface->cell_set_width (editor, value, unit, scope); +} + +gint +e_content_editor_cell_get_width (EContentEditor *editor, + EContentEditorUnit *unit) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + g_return_val_if_fail (unit != NULL, 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->cell_get_width != NULL, 0); + + return iface->cell_get_width (editor, unit); +} + +void +e_content_editor_cell_set_row_span (EContentEditor *editor, + gint value, + EContentEditorScope scope) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_row_span != NULL); + + iface->cell_set_row_span (editor, value, scope); +} + +gint +e_content_editor_cell_get_row_span (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->cell_get_row_span != NULL, 0); + + return iface->cell_get_row_span (editor); +} + +void +e_content_editor_cell_set_col_span (EContentEditor *editor, + gint value, + EContentEditorScope scope) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_col_span != NULL); + + iface->cell_set_col_span (editor, value, scope); +} + +gint +e_content_editor_cell_get_col_span (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->cell_get_col_span != NULL, 0); + + return iface->cell_get_col_span (editor); +} + +/* uri could be NULL -> removes the current image */ +void +e_content_editor_cell_set_background_image_uri (EContentEditor *editor, + const gchar *uri) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_background_image_uri != NULL); + + iface->cell_set_background_image_uri (editor, uri); +} + +gchar * +e_content_editor_cell_get_background_image_uri (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->cell_get_background_image_uri != NULL, NULL); + + return iface->cell_get_background_image_uri (editor); +} + +void +e_content_editor_cell_set_background_color (EContentEditor *editor, + const GdkRGBA *value, + EContentEditorScope scope) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + g_return_if_fail (value != NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_set_background_color != NULL); + + iface->cell_set_background_color (editor, value, scope); +} + +void +e_content_editor_cell_get_background_color (EContentEditor *editor, + GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->cell_get_background_color != NULL); + + iface->cell_get_background_color (editor, value); +} + +void +e_content_editor_table_set_row_count (EContentEditor *editor, + guint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_row_count != NULL); + + iface->table_set_row_count (editor, value); +} + +guint +e_content_editor_table_get_row_count (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->table_get_row_count != NULL, 0); + + return iface->table_get_row_count (editor); +} + +void +e_content_editor_table_set_column_count (EContentEditor *editor, + guint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_column_count != NULL); + + iface->table_set_column_count (editor, value); +} + +guint +e_content_editor_table_get_column_count (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->table_get_column_count != NULL, 0); + + return iface->table_get_column_count (editor); +} + +void +e_content_editor_table_set_width (EContentEditor *editor, + gint value, + EContentEditorUnit unit) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_width != NULL); + + iface->table_set_width (editor, value, unit); +} + +guint +e_content_editor_table_get_width (EContentEditor *editor, + EContentEditorUnit *unit) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->table_get_width != NULL, 0); + + return iface->table_get_width (editor, unit); +} + +void +e_content_editor_table_set_align (EContentEditor *editor, + const gchar *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_align != NULL); + + iface->table_set_align (editor, value); +} + +gchar * +e_content_editor_table_get_align (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->table_get_align != NULL, NULL); + + return iface->table_get_align (editor); +} + +void +e_content_editor_table_set_padding (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_padding != NULL); + + iface->table_set_padding (editor, value); +} + +gint +e_content_editor_table_get_padding (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->table_get_padding != NULL, 0); + + return iface->table_get_padding (editor); +} + +void +e_content_editor_table_set_spacing (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_spacing != NULL); + + iface->table_set_spacing (editor, value); +} + +gint +e_content_editor_table_get_spacing (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->table_get_spacing != NULL, 0); + + return iface->table_get_spacing (editor); +} + +void +e_content_editor_table_set_border (EContentEditor *editor, + gint value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_border != NULL); + + iface->table_set_border (editor, value); +} + +gint +e_content_editor_table_get_border (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->table_get_border != NULL, 0); + + return iface->table_get_border (editor); +} + +gchar * +e_content_editor_table_get_background_image_uri (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->table_get_background_image_uri != NULL, NULL); + + return iface->table_get_background_image_uri (editor); +} + +void +e_content_editor_table_set_background_image_uri (EContentEditor *editor, + const gchar *uri) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_background_image_uri != NULL); + + iface->table_set_background_image_uri (editor, uri); +} + +void +e_content_editor_table_get_background_color (EContentEditor *editor, + GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_get_background_color != NULL); + + iface->table_get_background_color (editor, value); +} + +void +e_content_editor_table_set_background_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->table_set_background_color != NULL); + + iface->table_set_background_color (editor, value); +} + +gboolean +e_content_editor_on_table_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->on_table_dialog_open != NULL, FALSE); + + return iface->on_table_dialog_open (editor); +} + +void +e_content_editor_on_table_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_table_dialog_close != NULL); + + iface->on_table_dialog_close (editor); +} + +void +e_content_editor_on_spell_check_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_spell_check_dialog_close != NULL); + + iface->on_spell_check_dialog_close (editor); +} + +void +e_content_editor_on_spell_check_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_spell_check_dialog_close != NULL); + + iface->on_spell_check_dialog_close (editor); +} + +gchar * +e_content_editor_spell_check_next_word (EContentEditor *editor, + const gchar *word) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->spell_check_next_word != NULL, NULL); + + return iface->spell_check_next_word (editor, word); +} + +gchar * +e_content_editor_spell_check_prev_word (EContentEditor *editor, + const gchar *word) +{ + EContentEditorInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->spell_check_prev_word != NULL, NULL); + + return iface->spell_check_prev_word (editor, word); +} + +void +e_content_editor_on_replace_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_replace_dialog_open != NULL); + + iface->on_replace_dialog_open (editor); +} + +void +e_content_editor_on_replace_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_replace_dialog_close != NULL); + + iface->on_replace_dialog_close (editor); +} + +void +e_content_editor_on_find_dialog_open (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_find_dialog_open != NULL); + + iface->on_find_dialog_open (editor); +} + +void +e_content_editor_on_find_dialog_close (EContentEditor *editor) +{ + EContentEditorInterface *iface; + + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + iface = E_CONTENT_EDITOR_GET_IFACE (editor); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->on_find_dialog_close != NULL); + + iface->on_find_dialog_close (editor); +} + +void +e_content_editor_emit_load_finished (EContentEditor *editor) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_signal_emit (editor, signals[LOAD_FINISHED], 0); +} + +gboolean +e_content_editor_emit_paste_clipboard (EContentEditor *editor) +{ + gboolean handled = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_signal_emit (editor, signals[PASTE_CLIPBOARD], 0, &handled); + + return handled; +} + +gboolean +e_content_editor_emit_paste_primary_clipboard (EContentEditor *editor) +{ + gboolean handled = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_signal_emit (editor, signals[PASTE_PRIMARY_CLIPBOARD], 0, &handled); + + return handled; +} + +gboolean +e_content_editor_emit_context_menu_requested (EContentEditor *editor, + EContentEditorNodeFlags flags, + GdkEvent *event) +{ + gboolean handled = FALSE; + + g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE); + + g_signal_emit (editor, signals[CONTEXT_MENU_REQUESTED], 0, flags, event, &handled); + + return handled; +} + +void +e_content_editor_emit_find_done (EContentEditor *editor, + guint match_count) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_signal_emit (editor, signals[FIND_DONE], 0, match_count); +} + +void +e_content_editor_emit_replace_all_done (EContentEditor *editor, + guint replaced_count) +{ + g_return_if_fail (E_IS_CONTENT_EDITOR (editor)); + + g_signal_emit (editor, signals[REPLACE_ALL_DONE], 0, replaced_count); +} diff --git a/e-util/e-content-editor.h b/e-util/e-content-editor.h new file mode 100644 index 0000000..8d3d64e --- /dev/null +++ b/e-util/e-content-editor.h @@ -0,0 +1,1021 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION) +#error "Only <e-util/e-util.h> should be included directly." +#endif + +#ifndef E_CONTENT_EDITOR_H +#define E_CONTENT_EDITOR_H + +#include <glib-object.h> +#include <gtk/gtk.h> + +#include <camel/camel.h> + +#include <e-util/e-emoticon.h> +#include <e-util/e-spell-checker.h> +#include <e-util/e-util-enums.h> + +#define DEFAULT_CONTENT_EDITOR_NAME "WebKit" + +G_BEGIN_DECLS + +struct _EHTMLEditor; + +#define E_TYPE_CONTENT_EDITOR e_content_editor_get_type () +G_DECLARE_INTERFACE (EContentEditor, e_content_editor, E, CONTENT_EDITOR, GtkWidget) + +typedef void (*EContentEditorInitializedCallback) (EContentEditor *content_editor, + gpointer user_data); + +struct _EContentEditorInterface { + GTypeInterface parent_interface; + + void (*initialize) (EContentEditor *content_editor, + EContentEditorInitializedCallback callback, + gpointer user_data); + void (*setup_editor) (EContentEditor *content_editor, + struct _EHTMLEditor *html_editor); + void (*update_styles) (EContentEditor *editor); + void (*insert_content) (EContentEditor *editor, + const gchar *content, + EContentEditorInsertContentFlags flags); + + gchar * (*get_content) (EContentEditor *editor, + EContentEditorGetContentFlags flags, + const gchar *inline_images_from_domain, + GSList **inline_images_parts /* newly created CamelMimePart * */); + + void (*insert_image) (EContentEditor *editor, + const gchar *uri); + + void (*insert_image_from_mime_part) + (EContentEditor *editor, + CamelMimePart *part); + + void (*insert_emoticon) (EContentEditor *editor, + EEmoticon *emoticon); + + void (*move_caret_on_coordinates) (EContentEditor *editor, + gint x, + gint y, + gboolean cancel_if_not_collapsed); + + void (*cut) (EContentEditor *editor); + + void (*copy) (EContentEditor *editor); + + void (*paste) (EContentEditor *editor); + + void (*paste_primary) (EContentEditor *editor); + + void (*undo) (EContentEditor *editor); + + void (*redo) (EContentEditor *editor); + + void (*clear_undo_redo_history) (EContentEditor *editor); + + void (*set_spell_checking_languages) (EContentEditor *editor, + const gchar **languages); + + gchar * (*get_selected_text) (EContentEditor *editor); + + gchar * (*get_caret_word) (EContentEditor *editor); + + void (*replace_caret_word) (EContentEditor *editor, + const gchar *replacement); + + void (*select_all) (EContentEditor *editor); + + void (*selection_indent) (EContentEditor *editor); + + void (*selection_unindent) (EContentEditor *editor); + + void (*selection_create_link) (EContentEditor *editor, + const gchar *uri); + + void (*selection_unlink) (EContentEditor *editor); + + void (*find) (EContentEditor *editor, + guint32 flags, + const gchar *text); + + void (*replace) (EContentEditor *editor, + const gchar *replacement); + + void (*replace_all) (EContentEditor *editor, + guint32 flags, + const gchar *find_text, + const gchar *replace_with); + + void (*selection_save) (EContentEditor *editor); + + void (*selection_restore) (EContentEditor *editor); + + void (*selection_wrap) (EContentEditor *editor); + + guint (*get_caret_position) (EContentEditor *editor); + + guint (*get_caret_offset) (EContentEditor *editor); + + gchar * (*get_current_signature_uid) (EContentEditor *editor); + + gboolean (*is_ready) (EContentEditor *editor); + + gchar * (*insert_signature) (EContentEditor *editor, + const gchar *content, + gboolean is_html, + const gchar *signature_id, + gboolean *set_signature_from_message, + gboolean *check_if_signature_is_changed, + gboolean *ignore_next_signature_change); + + void (*delete_cell_contents) (EContentEditor *editor); + + void (*delete_column) (EContentEditor *editor); + + void (*delete_row) (EContentEditor *editor); + + void (*delete_table) (EContentEditor *editor); + + void (*insert_column_after) (EContentEditor *editor); + + void (*insert_column_before) (EContentEditor *editor); + + void (*insert_row_above) (EContentEditor *editor); + + void (*insert_row_below) (EContentEditor *editor); + + gboolean (*on_h_rule_dialog_open) (EContentEditor *editor); + + void (*on_h_rule_dialog_close) (EContentEditor *editor); + + void (*h_rule_set_align) (EContentEditor *editor, + const gchar *value); + + gchar * (*h_rule_get_align) (EContentEditor *editor); + + void (*h_rule_set_size) (EContentEditor *editor, + gint value); + + gint (*h_rule_get_size) (EContentEditor *editor); + + void (*h_rule_set_width) (EContentEditor *editor, + gint value, + EContentEditorUnit unit); + + gint (*h_rule_get_width) (EContentEditor *editor, + EContentEditorUnit *unit); + + void (*h_rule_set_no_shade) (EContentEditor *editor, + gboolean value); + + gboolean (*h_rule_get_no_shade) (EContentEditor *editor); + + void (*on_image_dialog_open) (EContentEditor *editor); + + void (*on_image_dialog_close) (EContentEditor *editor); + + void (*image_set_src) (EContentEditor *editor, + const gchar *value); + + gchar * (*image_get_src) (EContentEditor *editor); + + void (*image_set_alt) (EContentEditor *editor, + const gchar *value); + + gchar * (*image_get_alt) (EContentEditor *editor); + + gint32 (*image_get_natural_width) (EContentEditor *editor); + + gint32 (*image_get_width) (EContentEditor *editor); + + void (*image_set_width) (EContentEditor *editor, + gint value); + + void (*image_set_width_follow) (EContentEditor *editor, + gboolean value); + + gint32 (*image_get_natural_height) (EContentEditor *editor); + + gint32 (*image_get_height) (EContentEditor *editor); + + void (*image_set_height) (EContentEditor *editor, + gint value); + + void (*image_set_height_follow) (EContentEditor *editor, + gboolean value); + + void (*image_set_url) (EContentEditor *editor, + const gchar *value); + + gchar * (*image_get_url) (EContentEditor *editor); + + void (*image_set_vspace) (EContentEditor *editor, + gint value); + + gint (*image_get_vspace) (EContentEditor *editor); + + void (*image_set_hspace) (EContentEditor *editor, + gint value); + + gint (*image_get_hspace) (EContentEditor *editor); + + void (*image_set_border) (EContentEditor *editor, + gint border); + + gint (*image_get_border) (EContentEditor *editor); + + void (*image_set_align) (EContentEditor *editor, + const gchar *value); + + gchar * (*image_get_align) (EContentEditor *editor); + + void (*on_link_dialog_open) (EContentEditor *editor); + + void (*on_link_dialog_close) (EContentEditor *editor); + + void (*link_get_values) (EContentEditor *editor, + gchar **href, + gchar **text); + + void (*link_set_values) (EContentEditor *editor, + const gchar *href, + const gchar *text); + + void (*on_page_dialog_open) (EContentEditor *editor); + + void (*on_page_dialog_close) (EContentEditor *editor); + + void (*page_set_text_color) (EContentEditor *editor, + const GdkRGBA *value); + + void (*page_get_text_color) (EContentEditor *editor, + GdkRGBA *value); + + void (*page_set_background_color) (EContentEditor *editor, + const GdkRGBA *value); + + void (*page_get_background_color) (EContentEditor *editor, + GdkRGBA *value); + + void (*page_set_link_color) (EContentEditor *editor, + const GdkRGBA *value); + + void (*page_get_link_color) (EContentEditor *editor, + GdkRGBA *value); + + void (*page_set_visited_link_color) (EContentEditor *editor, + const GdkRGBA *value); + + void (*page_get_visited_link_color) (EContentEditor *editor, + GdkRGBA *value); + + void (*page_set_background_image_uri) + (EContentEditor *editor, + const gchar *uri); + + gchar * (*page_get_background_image_uri) + (EContentEditor *editor); + + void (*on_cell_dialog_open) (EContentEditor *editor); + + void (*on_cell_dialog_close) (EContentEditor *editor); + + void (*cell_set_v_align) (EContentEditor *editor, + const gchar *value, + EContentEditorScope scope); + + gchar * (*cell_get_v_align) (EContentEditor *editor); + + void (*cell_set_align) (EContentEditor *editor, + const gchar *value, + EContentEditorScope scope); + + gchar * (*cell_get_align) (EContentEditor *editor); + + void (*cell_set_wrap) (EContentEditor *editor, + gboolean value, + EContentEditorScope scope); + + gboolean (*cell_get_wrap) (EContentEditor *editor); + + void (*cell_set_header_style) (EContentEditor *editor, + gboolean value, + EContentEditorScope scope); + + gboolean (*cell_is_header) (EContentEditor *editor); + + void (*cell_set_width) (EContentEditor *editor, + gint value, + EContentEditorUnit unit, + EContentEditorScope scope); + + gint (*cell_get_width) (EContentEditor *editor, + EContentEditorUnit *unit); + + void (*cell_set_row_span) (EContentEditor *editor, + gint value, + EContentEditorScope scope); + + gint (*cell_get_row_span) (EContentEditor *editor); + + void (*cell_set_col_span) (EContentEditor *editor, + gint value, + EContentEditorScope scope); + + gint (*cell_get_col_span) (EContentEditor *editor); + + gchar * (*cell_get_background_image_uri) + (EContentEditor *editor); + + void (*cell_set_background_image_uri) + (EContentEditor *editor, + const gchar *uri); + + void (*cell_get_background_color) (EContentEditor *editor, + GdkRGBA *value); + + void (*cell_set_background_color) (EContentEditor *editor, + const GdkRGBA *value, + EContentEditorScope scope); + + void (*table_set_row_count) (EContentEditor *editor, + guint value); + + guint (*table_get_row_count) (EContentEditor *editor); + + void (*table_set_column_count) (EContentEditor *editor, + guint value); + + guint (*table_get_column_count) (EContentEditor *editor); + + void (*table_set_width) (EContentEditor *editor, + gint value, + EContentEditorUnit unit); + + guint (*table_get_width) (EContentEditor *editor, + EContentEditorUnit *unit); + + void (*table_set_align) (EContentEditor *editor, + const gchar *value); + + gchar * (*table_get_align) (EContentEditor *editor); + + void (*table_set_padding) (EContentEditor *editor, + gint value); + + gint (*table_get_padding) (EContentEditor *editor); + + void (*table_set_spacing) (EContentEditor *editor, + gint value); + + gint (*table_get_spacing) (EContentEditor *editor); + + void (*table_set_border) (EContentEditor *editor, + gint value); + + gint (*table_get_border) (EContentEditor *editor); + + gchar * (*table_get_background_image_uri) + (EContentEditor *editor); + + void (*table_set_background_image_uri) + (EContentEditor *editor, + const gchar *uri); + + void (*table_get_background_color) (EContentEditor *editor, + GdkRGBA *value); + + void (*table_set_background_color) (EContentEditor *editor, + const GdkRGBA *value); + + gboolean (*on_table_dialog_open) (EContentEditor *editor); + + void (*on_table_dialog_close) (EContentEditor *editor); + + void (*on_spell_check_dialog_open) (EContentEditor *editor); + + void (*on_spell_check_dialog_close) (EContentEditor *editor); + + gchar * (*spell_check_next_word) (EContentEditor *editor, + const gchar *word); + + gchar * (*spell_check_prev_word) (EContentEditor *editor, + const gchar *word); + + void (*on_replace_dialog_open) (EContentEditor *editor); + + void (*on_replace_dialog_close) (EContentEditor *editor); + + void (*on_find_dialog_open) (EContentEditor *editor); + + void (*on_find_dialog_close) (EContentEditor *editor); + + /* Signals */ + void (*load_finished) (EContentEditor *editor); + gboolean (*paste_clipboard) (EContentEditor *editor); + gboolean (*paste_primary_clipboard) (EContentEditor *editor); + gboolean (*context_menu_requested) (EContentEditor *editor, + EContentEditorNodeFlags flags, + GdkEvent *event); + void (*find_done) (EContentEditor *editor, + guint match_count); + void (*replace_all_done) (EContentEditor *editor, + guint replaced_count); +}; + +/* Properties */ + +ESpellChecker * e_content_editor_ref_spell_checker + (EContentEditor *editor); +gboolean e_content_editor_can_cut (EContentEditor *editor); +gboolean e_content_editor_can_copy (EContentEditor *editor); +gboolean e_content_editor_can_paste (EContentEditor *editor); +gboolean e_content_editor_can_undo (EContentEditor *editor); +gboolean e_content_editor_can_redo (EContentEditor *editor); +gboolean e_content_editor_is_indented (EContentEditor *editor); +gboolean e_content_editor_get_spell_check_enabled + (EContentEditor *editor); +void e_content_editor_set_spell_check_enabled + (EContentEditor *editor, + gboolean enable); +gboolean e_content_editor_is_editable (EContentEditor *editor); +void e_content_editor_set_editable (EContentEditor *editor, + gboolean editable); +gboolean e_content_editor_get_changed (EContentEditor *editor); +void e_content_editor_set_changed (EContentEditor *editor, + gboolean changed); +gboolean e_content_editor_get_html_mode (EContentEditor *editor); +void e_content_editor_set_html_mode (EContentEditor *editor, + gboolean html_mode); +void e_content_editor_set_alignment (EContentEditor *editor, + EContentEditorAlignment value); +EContentEditorAlignment + e_content_editor_get_alignment (EContentEditor *editor); +void e_content_editor_set_background_color + (EContentEditor *editor, + const GdkRGBA *value); +GdkRGBA * e_content_editor_dup_background_color + (EContentEditor *editor); +void e_content_editor_set_font_color (EContentEditor *editor, + const GdkRGBA *value); +GdkRGBA * e_content_editor_dup_font_color (EContentEditor *editor); +void e_content_editor_set_font_name (EContentEditor *editor, + const gchar *value); +gchar * e_content_editor_dup_font_name (EContentEditor *editor); +void e_content_editor_set_font_size (EContentEditor *editor, + gint value); +gint e_content_editor_get_font_size (EContentEditor *editor); +void e_content_editor_set_block_format + (EContentEditor *editor, + EContentEditorBlockFormat value); +EContentEditorBlockFormat + e_content_editor_get_block_format + (EContentEditor *editor); +void e_content_editor_set_bold (EContentEditor *editor, + gboolean bold); +gboolean e_content_editor_is_bold (EContentEditor *editor); +void e_content_editor_set_italic (EContentEditor *editor, + gboolean italic); +gboolean e_content_editor_is_italic (EContentEditor *editor); +void e_content_editor_set_monospaced (EContentEditor *editor, + gboolean monospaced); +gboolean e_content_editor_is_monospaced (EContentEditor *editor); +void e_content_editor_set_strikethrough + (EContentEditor *editor, + gboolean strikethrough); +gboolean e_content_editor_is_strikethrough + (EContentEditor *editor); +void e_content_editor_set_subscript (EContentEditor *editor, + gboolean subscript); +gboolean e_content_editor_is_subscript (EContentEditor *editor); +void e_content_editor_set_superscript + (EContentEditor *editor, + gboolean superscript); +gboolean e_content_editor_is_superscript + (EContentEditor *editor); +void e_content_editor_set_underline (EContentEditor *editor, + gboolean underline); +gboolean e_content_editor_is_underline (EContentEditor *editor); + +/* Methods */ +void e_content_editor_initialize (EContentEditor *content_editor, + EContentEditorInitializedCallback callback, + gpointer user_data); +void e_content_editor_setup_editor (EContentEditor *content_editor, + struct _EHTMLEditor *html_editor); +void e_content_editor_update_styles (EContentEditor *editor); +void e_content_editor_insert_content (EContentEditor *editor, + const gchar *content, + EContentEditorInsertContentFlags flags); + +gchar * e_content_editor_get_content (EContentEditor *editor, + EContentEditorGetContentFlags flags, + const gchar *inline_images_from_domain, + GSList **inline_images_parts /* newly created CamelMimePart * */); + +void e_content_editor_insert_image_from_mime_part + (EContentEditor *editor, + CamelMimePart *part); + +void e_content_editor_insert_image (EContentEditor *editor, + const gchar *uri); + +void e_content_editor_insert_emoticon + (EContentEditor *editor, + EEmoticon *emoticon); + +void e_content_editor_move_caret_on_coordinates + (EContentEditor *editor, + gint x, + gint y, + gboolean cancel_if_not_collapsed); + +void e_content_editor_cut (EContentEditor *editor); + +void e_content_editor_copy (EContentEditor *editor); + +void e_content_editor_paste (EContentEditor *editor); + +void e_content_editor_paste_primary (EContentEditor *editor); + +void e_content_editor_undo (EContentEditor *editor); + +void e_content_editor_redo (EContentEditor *editor); + +void e_content_editor_clear_undo_redo_history + (EContentEditor *editor); + +void e_content_editor_set_spell_checking_languages + (EContentEditor *editor, + const gchar **languages); + +void e_content_editor_select_all (EContentEditor *editor); + +gchar * e_content_editor_get_selected_text + (EContentEditor *editor); + +gchar * e_content_editor_get_caret_word (EContentEditor *editor); + +void e_content_editor_replace_caret_word + (EContentEditor *editor, + const gchar *replacement); + +void e_content_editor_selection_indent + (EContentEditor *editor); + +void e_content_editor_selection_unindent + (EContentEditor *editor); + +void e_content_editor_selection_create_link + (EContentEditor *editor, + const gchar *uri); + +void e_content_editor_selection_unlink + (EContentEditor *editor); + +void e_content_editor_find (EContentEditor *editor, + guint32 flags, + const gchar *text); + +void e_content_editor_replace (EContentEditor *editor, + const gchar *replacement); + +void e_content_editor_replace_all (EContentEditor *editor, + guint32 flags, + const gchar *find_text, + const gchar *replace_with); + +void e_content_editor_selection_save (EContentEditor *editor); + +void e_content_editor_selection_restore + (EContentEditor *editor); + +void e_content_editor_selection_wrap (EContentEditor *editor); + +guint e_content_editor_get_caret_position + (EContentEditor *editor); + +guint e_content_editor_get_caret_offset + (EContentEditor *editor); + +gchar * e_content_editor_get_current_signature_uid + (EContentEditor *editor); + +gboolean e_content_editor_is_ready (EContentEditor *editor); + +gchar * e_content_editor_insert_signature + (EContentEditor *editor, + const gchar *content, + gboolean is_html, + const gchar *signature_id, + gboolean *set_signature_from_message, + gboolean *check_if_signature_is_changed, + gboolean *ignore_next_signature_change); + +void e_content_editor_delete_cell_contents + (EContentEditor *editor); +void + e_content_editor_delete_column (EContentEditor *editor); + +void e_content_editor_delete_row (EContentEditor *editor); + +void e_content_editor_delete_table (EContentEditor *editor); + +void e_content_editor_insert_column_after + (EContentEditor *editor); + +void e_content_editor_insert_column_before + (EContentEditor *editor); + +void e_content_editor_insert_row_above + (EContentEditor *editor); + +void e_content_editor_insert_row_below + (EContentEditor *editor); + +gboolean e_content_editor_on_h_rule_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_h_rule_dialog_close + (EContentEditor *editor); + +void e_content_editor_h_rule_set_align + (EContentEditor *editor, + const gchar *value); + +gchar * e_content_editor_h_rule_get_align + (EContentEditor *editor); + +void e_content_editor_h_rule_set_size + (EContentEditor *editor, + gint value); + +gint e_content_editor_h_rule_get_size + (EContentEditor *editor); + +void e_content_editor_h_rule_set_width + (EContentEditor *editor, + gint value, + EContentEditorUnit unit); + +gint e_content_editor_h_rule_get_width + (EContentEditor *editor, + EContentEditorUnit *unit); + +void e_content_editor_h_rule_set_no_shade + (EContentEditor *editor, + gboolean value); + +gboolean e_content_editor_h_rule_get_no_shade + (EContentEditor *editor); + +void e_content_editor_on_image_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_image_dialog_close + (EContentEditor *editor); + +void e_content_editor_image_set_src (EContentEditor *editor, + const gchar *value); + +gchar * e_content_editor_image_get_src (EContentEditor *editor); + +void e_content_editor_image_set_alt (EContentEditor *editor, + const gchar *value); + +gchar * e_content_editor_image_get_alt (EContentEditor *editor); + +void e_content_editor_image_set_url (EContentEditor *editor, + const gchar *value); + +gchar * e_content_editor_image_get_url (EContentEditor *editor); + +void e_content_editor_image_set_vspace + (EContentEditor *editor, + gint value); + +gint e_content_editor_image_get_vspace + (EContentEditor *editor); + +void e_content_editor_image_set_hspace + (EContentEditor *editor, + gint value); + +gint e_content_editor_image_get_hspace + (EContentEditor *editor); + +void e_content_editor_image_set_border + (EContentEditor *editor, + gint value); + +gint e_content_editor_image_get_border + (EContentEditor *editor); + +void e_content_editor_image_set_align + (EContentEditor *editor, + const gchar *value); + +gchar * e_content_editor_image_get_align + (EContentEditor *editor); + +void e_content_editor_image_set_width + (EContentEditor *editor, + gint value); + +gint32 e_content_editor_image_get_width + (EContentEditor *editor); + +gint32 e_content_editor_image_get_natural_width + (EContentEditor *editor); + +void e_content_editor_image_set_width_follow + (EContentEditor *editor, + gboolean value); +void e_content_editor_image_set_height + (EContentEditor *editor, + gint value); + +gint32 e_content_editor_image_get_height + (EContentEditor *editor); + +gint32 e_content_editor_image_get_natural_height + (EContentEditor *editor); + +void e_content_editor_image_set_height_follow + (EContentEditor *editor, + gboolean value); + +void e_content_editor_on_link_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_link_dialog_close + (EContentEditor *editor); + +void e_content_editor_link_get_values + (EContentEditor *editor, + gchar **href, + gchar **text); + +void e_content_editor_link_set_values + (EContentEditor *editor, + const gchar *href, + const gchar *text); + +void e_content_editor_page_set_text_color + (EContentEditor *editor, + const GdkRGBA *value); + +void e_content_editor_on_page_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_page_dialog_close + (EContentEditor *editor); + +void e_content_editor_page_get_text_color + (EContentEditor *editor, + GdkRGBA *value); + +void e_content_editor_page_set_background_color + (EContentEditor *editor, + const GdkRGBA *value); + +void e_content_editor_page_get_background_color + (EContentEditor *editor, + GdkRGBA *value); + +void e_content_editor_page_set_link_color + (EContentEditor *editor, + const GdkRGBA *value); + +void e_content_editor_page_get_link_color + (EContentEditor *editor, + GdkRGBA *value); + +void e_content_editor_page_set_visited_link_color + (EContentEditor *editor, + const GdkRGBA *value); + +void e_content_editor_page_get_visited_link_color + (EContentEditor *editor, + GdkRGBA *value); + +void e_content_editor_page_set_background_image_uri + (EContentEditor *editor, + const gchar *uri); + +gchar * e_content_editor_page_get_background_image_uri + (EContentEditor *editor); + +void e_content_editor_on_cell_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_cell_dialog_close + (EContentEditor *editor); + +void e_content_editor_cell_set_v_align + (EContentEditor *editor, + const gchar *value, + EContentEditorScope scope); + +gchar * e_content_editor_cell_get_v_align + (EContentEditor *editor); + +void e_content_editor_cell_set_align (EContentEditor *editor, + const gchar *value, + EContentEditorScope scope); + +gchar * e_content_editor_cell_get_align (EContentEditor *editor); + +void e_content_editor_cell_set_wrap (EContentEditor *editor, + gboolean value, + EContentEditorScope scope); + +gboolean e_content_editor_cell_get_wrap (EContentEditor *editor); + +void e_content_editor_cell_set_header_style + (EContentEditor *editor, + gboolean value, + EContentEditorScope scope); + +gboolean e_content_editor_cell_is_header (EContentEditor *editor); + +void e_content_editor_cell_set_width (EContentEditor *editor, + gint value, + EContentEditorUnit unit, + EContentEditorScope scope); + +gint e_content_editor_cell_get_width (EContentEditor *editor, + EContentEditorUnit *unit); + +void e_content_editor_cell_set_row_span + (EContentEditor *editor, + gint value, + EContentEditorScope scope); + +gint e_content_editor_cell_get_row_span + (EContentEditor *editor); + +void e_content_editor_cell_set_col_span + (EContentEditor *editor, + gint value, + EContentEditorScope scope); + +gint e_content_editor_cell_get_col_span + (EContentEditor *editor); + +void e_content_editor_cell_set_background_image_uri + (EContentEditor *editor, + const gchar *uri); + +gchar * e_content_editor_cell_get_background_image_uri + (EContentEditor *editor); + +void e_content_editor_cell_set_background_color + (EContentEditor *editor, + const GdkRGBA *value, + EContentEditorScope scope); + +void e_content_editor_cell_get_background_color + (EContentEditor *editor, + GdkRGBA *value); + +void e_content_editor_table_set_row_count + (EContentEditor *editor, + guint value); + +guint e_content_editor_table_get_row_count + (EContentEditor *editor); + +void e_content_editor_table_set_column_count + (EContentEditor *editor, + guint value); + +guint e_content_editor_table_get_column_count + (EContentEditor *editor); + +void e_content_editor_table_set_width + (EContentEditor *editor, + gint value, + EContentEditorUnit unit); + +guint e_content_editor_table_get_width + (EContentEditor *editor, + EContentEditorUnit *unit); + +void e_content_editor_table_set_align + (EContentEditor *editor, + const gchar *value); + +gchar * e_content_editor_table_get_align + (EContentEditor *editor); + +void e_content_editor_table_set_padding + (EContentEditor *editor, + gint value); + +gint e_content_editor_table_get_padding + (EContentEditor *editor); + +void e_content_editor_table_set_spacing + (EContentEditor *editor, + gint value); + +gint e_content_editor_table_get_spacing + (EContentEditor *editor); + +void e_content_editor_table_set_border + (EContentEditor *editor, + gint value); + +gint e_content_editor_table_get_border + (EContentEditor *editor); + +gchar * e_content_editor_table_get_background_image_uri + (EContentEditor *editor); + +void e_content_editor_table_set_background_image_uri + (EContentEditor *editor, + const gchar *uri); + +void e_content_editor_table_get_background_color + (EContentEditor *editor, + GdkRGBA *value); + +void e_content_editor_table_set_background_color + (EContentEditor *editor, + const GdkRGBA *value); + +gboolean e_content_editor_on_table_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_table_dialog_close + (EContentEditor *editor); + +void e_content_editor_on_spell_check_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_spell_check_dialog_close + (EContentEditor *editor); + +gchar * e_content_editor_spell_check_next_word + (EContentEditor *editor, + const gchar *word); + +gchar * e_content_editor_spell_check_prev_word + (EContentEditor *editor, + const gchar *word); + +void e_content_editor_spell_check_replace_all + (EContentEditor *editor, + const gchar *word, + const gchar *replacement); + +void e_content_editor_on_replace_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_replace_dialog_close + (EContentEditor *editor); + +void e_content_editor_on_find_dialog_open + (EContentEditor *editor); + +void e_content_editor_on_find_dialog_close + (EContentEditor *editor); + +/* Signal helpers */ + +void e_content_editor_emit_load_finished + (EContentEditor *editor); +gboolean e_content_editor_emit_paste_clipboard + (EContentEditor *editor); +gboolean e_content_editor_emit_paste_primary_clipboard + (EContentEditor *editor); +gboolean e_content_editor_emit_context_menu_requested + (EContentEditor *editor, + EContentEditorNodeFlags flags, + GdkEvent *event); +void e_content_editor_emit_find_done (EContentEditor *editor, + guint match_count); +void e_content_editor_emit_replace_all_done + (EContentEditor *editor, + guint replaced_count); + +G_END_DECLS + +#endif /* E_CONTENT_EDITOR_H */ diff --git a/e-util/e-content-request.c b/e-util/e-content-request.c new file mode 100644 index 0000000..9448d44 --- /dev/null +++ b/e-util/e-content-request.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include "e-content-request.h" + +G_DEFINE_INTERFACE (EContentRequest, e_content_request, G_TYPE_OBJECT) + +static void +e_content_request_default_init (EContentRequestInterface *iface) +{ +} + +gboolean +e_content_request_can_process_uri (EContentRequest *request, + const gchar *uri) +{ + EContentRequestInterface *iface; + + g_return_val_if_fail (E_IS_CONTENT_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + + iface = E_CONTENT_REQUEST_GET_INTERFACE (request); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->can_process_uri != NULL, FALSE); + + return iface->can_process_uri (request, uri); +} + +gboolean +e_content_request_process_sync (EContentRequest *request, + const gchar *uri, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error) +{ + EContentRequestInterface *iface; + GError *local_error = NULL; + + g_return_val_if_fail (E_IS_CONTENT_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + g_return_val_if_fail (G_IS_OBJECT (requester), FALSE); + g_return_val_if_fail (out_stream != NULL, FALSE); + g_return_val_if_fail (out_stream_length != NULL, FALSE); + g_return_val_if_fail (out_mime_type != NULL, FALSE); + + iface = E_CONTENT_REQUEST_GET_INTERFACE (request); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->process_sync != NULL, FALSE); + + if (!iface->process_sync (request, uri, requester, out_stream, out_stream_length, out_mime_type, cancellable, &local_error)) { + if (!local_error) + local_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, g_strerror (ENOENT)); + + g_propagate_error (error, local_error); + + return FALSE; + } + + return TRUE; +} + +typedef struct _ThreadData +{ + gchar *uri; + GObject *requester; + GInputStream *out_stream; + gint64 out_stream_length; + gchar *out_mime_type; +} ThreadData; + +static void +thread_data_free (gpointer ptr) +{ + ThreadData *td = ptr; + + if (td) { + g_clear_object (&td->out_stream); + g_clear_object (&td->requester); + g_free (td->uri); + g_free (td->out_mime_type); + g_free (td); + } +} + +static void +content_request_process_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + ThreadData *td = task_data; + GError *local_error = NULL; + + g_return_if_fail (E_IS_CONTENT_REQUEST (source_object)); + g_return_if_fail (td != NULL); + + if (!e_content_request_process_sync (E_CONTENT_REQUEST (source_object), + td->uri, td->requester, &td->out_stream, &td->out_stream_length, &td->out_mime_type, + cancellable, &local_error)) { + g_task_return_error (task, local_error); + } else { + g_task_return_boolean (task, TRUE); + } +} + +void +e_content_request_process (EContentRequest *request, + const gchar *uri, + GObject *requester, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + ThreadData *td; + + g_return_if_fail (E_IS_CONTENT_REQUEST (request)); + g_return_if_fail (uri != NULL); + g_return_if_fail (G_IS_OBJECT (requester)); + + td = g_new0 (ThreadData, 1); + td->uri = g_strdup (uri); + td->requester = g_object_ref (requester); + + task = g_task_new (request, cancellable, callback, user_data); + g_task_set_task_data (task, td, thread_data_free); + g_task_run_in_thread (task, content_request_process_thread); + g_object_unref (task); +} + +gboolean +e_content_request_process_finish (EContentRequest *request, + GAsyncResult *result, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GError **error) +{ + ThreadData *td; + + g_return_val_if_fail (g_task_is_valid (result, request), FALSE); + g_return_val_if_fail (E_IS_CONTENT_REQUEST (request), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + g_return_val_if_fail (out_stream != NULL, FALSE); + g_return_val_if_fail (out_stream_length != NULL, FALSE); + g_return_val_if_fail (out_mime_type != NULL, FALSE); + + td = g_task_get_task_data (G_TASK (result)); + g_return_val_if_fail (td != NULL, FALSE); + + if (!g_task_propagate_boolean (G_TASK (result), error)) + return FALSE; + + *out_stream = td->out_stream; + *out_stream_length = td->out_stream_length; + *out_mime_type = td->out_mime_type; + + td->out_stream = NULL; + td->out_mime_type = NULL; + + return TRUE; +} diff --git a/e-util/e-content-request.h b/e-util/e-content-request.h new file mode 100644 index 0000000..7df94c4 --- /dev/null +++ b/e-util/e-content-request.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION) +#error "Only <e-util/e-util.h> should be included directly." +#endif + +#ifndef E_CONTENT_REQUEST_H +#define E_CONTENT_REQUEST_H + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +/* Standard GObject macros */ +#define E_TYPE_CONTENT_REQUEST \ + (e_content_request_get_type ()) +#define E_CONTENT_REQUEST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CONTENT_REQUEST, EContentRequest)) +#define E_CONTENT_REQUEST_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CONTENT_REQUEST, EContentRequestInterface)) +#define E_IS_CONTENT_REQUEST(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CONTENT_REQUEST)) +#define E_IS_CONTENT_REQUEST_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CONTENT_REQUEST)) +#define E_CONTENT_REQUEST_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE \ + ((obj), E_TYPE_CONTENT_REQUEST, EContentRequestInterface)) + +G_BEGIN_DECLS + +typedef struct _EContentRequest EContentRequest; +typedef struct _EContentRequestInterface EContentRequestInterface; + +struct _EContentRequestInterface { + GTypeInterface parent_interface; + + gboolean (* can_process_uri) (EContentRequest *request, + const gchar *uri); + gboolean (* process_sync) (EContentRequest *request, + const gchar *uri, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error); +}; + +GType e_content_request_get_type (void); +gboolean e_content_request_can_process_uri (EContentRequest *request, + const gchar *uri); +gboolean e_content_request_process_sync (EContentRequest *request, + const gchar *uri, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error); +void e_content_request_process (EContentRequest *request, + const gchar *uri, + GObject *requester, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_content_request_process_finish (EContentRequest *request, + GAsyncResult *result, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GError **error); + +G_END_DECLS + +#endif /* E_CONTENT_REQUEST_H */ diff --git a/e-util/e-emoticon.c b/e-util/e-emoticon.c index cc2fd41..53a9f49 100644 --- a/e-util/e-emoticon.c +++ b/e-util/e-emoticon.c @@ -121,3 +121,9 @@ e_emoticon_get_uri (EEmoticon *emoticon) return uri; } + +const gchar * +e_emoticon_get_name (EEmoticon *emoticon) +{ + return emoticon->icon_name; +} diff --git a/e-util/e-emoticon.h b/e-util/e-emoticon.h index e77806f..3b9e8c0 100644 --- a/e-util/e-emoticon.h +++ b/e-util/e-emoticon.h @@ -48,6 +48,7 @@ gboolean e_emoticon_equal (EEmoticon *emoticon_a, EEmoticon * e_emoticon_copy (EEmoticon *emoticon); void e_emoticon_free (EEmoticon *emoticon); gchar * e_emoticon_get_uri (EEmoticon *emoticon); +const gchar * e_emoticon_get_name (EEmoticon *emoticon); G_END_DECLS diff --git a/e-util/e-file-request.c b/e-util/e-file-request.c index f6b4ac2..8cdb18a 100644 --- a/e-util/e-file-request.c +++ b/e-util/e-file-request.c @@ -15,178 +15,124 @@ * */ -#define LIBSOUP_USE_UNSTABLE_REQUEST_API +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif -#include "e-file-request.h" +#include <stdio.h> +#include <string.h> #include <libsoup/soup.h> -#include <stdio.h> -#include <string.h> +#include "e-file-request.h" #define d(x) -#define E_FILE_REQUEST_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_FILE_REQUEST, EFileRequestPrivate)) - struct _EFileRequestPrivate { - gchar *content_type; - gint content_length; + gint dummy; }; -G_DEFINE_TYPE (EFileRequest, e_file_request, SOUP_TYPE_REQUEST) - -static void -handle_file_request (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) -{ - EFileRequest *request = E_FILE_REQUEST (object); - SoupURI *uri; - GInputStream *stream; - gchar *contents; - gsize length; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - uri = soup_request_get_uri (SOUP_REQUEST (request)); - - if (g_file_get_contents (uri->path, &contents, &length, NULL)) { +static void e_file_request_content_request_init (EContentRequestInterface *iface); - request->priv->content_type = - g_content_type_guess (uri->path, NULL, 0, NULL); - request->priv->content_length = length; - - stream = g_memory_input_stream_new_from_data ( - contents, length, (GDestroyNotify) g_free); - g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref); - } -} - -static void -file_request_finalize (GObject *object) -{ - EFileRequest *request = E_FILE_REQUEST (object); - - if (request->priv->content_type) { - g_free (request->priv->content_type); - request->priv->content_type = NULL; - } - - G_OBJECT_CLASS (e_file_request_parent_class)->finalize (object); -} +G_DEFINE_TYPE_WITH_CODE (EFileRequest, e_file_request, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_file_request_content_request_init)) static gboolean -file_request_check_uri (SoupRequest *request, - SoupURI *uri, - GError **error) -{ - return (strcmp (uri->scheme, "evo-file") == 0); -} - -static void -file_request_send_async (SoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +e_file_request_can_process_uri (EContentRequest *request, + const gchar *uri) { - GSimpleAsyncResult *simple; - - d ( - SoupURI *soup_uri = soup_request_get_uri (request); - gchar *uri = soup_uri_to_string (soup_uri, FALSE); - printf ("received request for %s\n", uri); - g_free (uri); - ); - - /* WebKit won't allow us to load data through local file:// protocol - * when using "remote" mail:// protocol, so we have evo-file:// - * which WebKit thinks it's remote, but in fact it behaves like - * oridnary file:// */ - - simple = g_simple_async_result_new ( - G_OBJECT (request), callback, user_data, - file_request_send_async); - - g_simple_async_result_set_check_cancellable (simple, cancellable); - - g_simple_async_result_run_in_thread ( - simple, handle_file_request, - G_PRIORITY_DEFAULT, cancellable); + g_return_val_if_fail (E_IS_FILE_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); - g_object_unref (simple); + return g_ascii_strncasecmp (uri, "evo-file:", 9) == 0; } -static GInputStream * -file_request_send_finish (SoupRequest *request, - GAsyncResult *result, - GError **error) +static gboolean +e_file_request_process_sync (EContentRequest *request, + const gchar *uri, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error) { - GSimpleAsyncResult *simple; - GInputStream *stream; - - simple = G_SIMPLE_ASYNC_RESULT (result); - stream = g_simple_async_result_get_op_res_gpointer (simple); - - /* Reset the stream before passing it back to webkit */ - if (stream && G_IS_SEEKABLE (stream)) - g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL); - - if (!stream) /* We must always return something */ - stream = g_memory_input_stream_new (); - else - g_object_ref (stream); - - return stream; -} + GFile *file; + GFileInputStream *file_input_stream; + GFileInfo *info; + goffset total_size; + SoupURI *suri; + + g_return_val_if_fail (E_IS_FILE_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + suri = soup_uri_new (uri); + g_return_val_if_fail (suri != NULL, FALSE); + + file = g_file_new_for_path (suri->path); + file_input_stream = g_file_read (file, cancellable, error); + + if (file_input_stream) { + total_size = -1; + info = g_file_input_stream_query_info (file_input_stream, G_FILE_ATTRIBUTE_STANDARD_SIZE, cancellable, NULL); + if (info) { + if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) + total_size = g_file_info_get_size (info); + g_object_unref (info); + } + + if (total_size == -1) { + info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, cancellable, NULL); + if (info) { + if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) + total_size = g_file_info_get_size (info); + g_object_unref (info); + } + } + } else { + total_size = -1; + } -static goffset -file_request_get_content_length (SoupRequest *request) -{ - EFileRequest *efr = E_FILE_REQUEST (request); + if (file_input_stream) { + *out_stream = G_INPUT_STREAM (file_input_stream); + *out_stream_length = (gint64) total_size; + *out_mime_type = g_content_type_guess (suri->path, NULL, 0, NULL); + } else { + *out_stream = NULL; + *out_stream_length = (gint64) total_size; + *out_mime_type = NULL; + } - d (printf ("Content-Length: %d bytes\n", efr->priv->content_length)); + g_object_unref (file); + soup_uri_free (suri); - return efr->priv->content_length; + return file_input_stream != NULL; } -static const gchar * -file_request_get_content_type (SoupRequest *request) +static void +e_file_request_content_request_init (EContentRequestInterface *iface) { - EFileRequest *efr = E_FILE_REQUEST (request); - - d (printf ("Content-Type: %s\n", efr->priv->content_type)); - - return efr->priv->content_type; + iface->can_process_uri = e_file_request_can_process_uri; + iface->process_sync = e_file_request_process_sync; } -static const gchar *data_schemes[] = { "evo-file", NULL }; - static void e_file_request_class_init (EFileRequestClass *class) { - GObjectClass *object_class; - SoupRequestClass *request_class; - g_type_class_add_private (class, sizeof (EFileRequestPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = file_request_finalize; - - request_class = SOUP_REQUEST_CLASS (class); - request_class->schemes = data_schemes; - request_class->send_async = file_request_send_async; - request_class->send_finish = file_request_send_finish; - request_class->get_content_type = file_request_get_content_type; - request_class->get_content_length = file_request_get_content_length; - request_class->check_uri = file_request_check_uri; } static void e_file_request_init (EFileRequest *request) { - request->priv = E_FILE_REQUEST_GET_PRIVATE (request); + request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_FILE_REQUEST, EFileRequestPrivate); } +EContentRequest * +e_file_request_new (void) +{ + return g_object_new (E_TYPE_FILE_REQUEST, NULL); +} diff --git a/e-util/e-file-request.h b/e-util/e-file-request.h index ab46c9a..706309d 100644 --- a/e-util/e-file-request.h +++ b/e-util/e-file-request.h @@ -22,10 +22,7 @@ #ifndef E_FILE_REQUEST_H #define E_FILE_REQUEST_H -#define LIBSOUP_USE_UNSTABLE_REQUEST_API - -#include <libsoup/soup.h> -#include <libsoup/soup-request.h> +#include <e-util/e-content-request.h> /* Standard GObject macros */ #define E_TYPE_FILE_REQUEST \ @@ -53,15 +50,17 @@ typedef struct _EFileRequestClass EFileRequestClass; typedef struct _EFileRequestPrivate EFileRequestPrivate; struct _EFileRequest { - SoupRequest parent; + GObject parent; EFileRequestPrivate *priv; }; struct _EFileRequestClass { - SoupRequestClass parent; + GObjectClass parent; }; GType e_file_request_get_type (void) G_GNUC_CONST; +EContentRequest * + e_file_request_new (void); G_END_DECLS diff --git a/e-util/e-focus-tracker.c b/e-util/e-focus-tracker.c index 7a9477a..794e56d 100644 --- a/e-util/e-focus-tracker.c +++ b/e-util/e-focus-tracker.c @@ -28,7 +28,7 @@ #include "e-selectable.h" #include "e-widget-undo.h" -#include "e-html-editor-view.h" +#include "e-content-editor.h" #define E_FOCUS_TRACKER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -257,7 +257,7 @@ focus_tracker_text_view_update_actions (EFocusTracker *focus_tracker, static void focus_tracker_editor_update_actions (EFocusTracker *focus_tracker, - EHTMLEditorView *view, + EContentEditor *cnt_editor, GdkAtom *targets, gint n_targets) { @@ -266,7 +266,7 @@ focus_tracker_editor_update_actions (EFocusTracker *focus_tracker, gboolean can_cut; gboolean can_paste; - g_object_get (view, + g_object_get (cnt_editor, "can-copy", &can_copy, "can-cut", &can_cut, "can-paste", &can_paste, @@ -368,9 +368,9 @@ focus_tracker_targets_received_cb (GtkClipboard *clipboard, focus_tracker, GTK_TEXT_VIEW (focus), targets, n_targets); - else if (E_IS_HTML_EDITOR_VIEW (focus)) + else if (E_IS_CONTENT_EDITOR (focus)) focus_tracker_editor_update_actions ( - focus_tracker, E_HTML_EDITOR_VIEW (focus), + focus_tracker, E_CONTENT_EDITOR (focus), targets, n_targets); g_object_unref (focus_tracker); @@ -391,7 +391,7 @@ focus_tracker_set_focus_cb (GtkWindow *window, if (GTK_IS_TEXT_VIEW (focus)) break; - if (E_IS_HTML_EDITOR_VIEW (focus)) + if (E_IS_CONTENT_EDITOR (focus)) break; focus = gtk_widget_get_parent (focus); diff --git a/e-util/e-html-editor-actions.c b/e-util/e-html-editor-actions.c index 638b05c..aaf9dd7 100644 --- a/e-util/e-html-editor-actions.c +++ b/e-util/e-html-editor-actions.c @@ -29,20 +29,20 @@ #include "e-html-editor.h" #include "e-html-editor-private.h" #include "e-html-editor-actions.h" -#include "e-html-editor-utils.h" #include "e-emoticon-action.h" #include "e-emoticon-chooser.h" #include "e-image-chooser-dialog.h" #include "e-spell-checker.h" #include "e-misc-utils.h" -#include "e-web-view.h" +#include "e-selection.h" +#include "e-content-editor.h" static void insert_html_file_ready_cb (GFile *file, GAsyncResult *result, EHTMLEditor *editor) { - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; gchar *contents = NULL; gsize length; GError *error = NULL; @@ -66,9 +66,10 @@ insert_html_file_ready_cb (GFile *file, return; } - selection = e_html_editor_view_get_selection ( - e_html_editor_get_view (editor)); - e_html_editor_selection_insert_html (selection, contents); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_content ( + cnt_editor, contents, E_CONTENT_EDITOR_INSERT_TEXT_HTML); + g_free (contents); g_object_unref (editor); @@ -79,7 +80,7 @@ insert_text_file_ready_cb (GFile *file, GAsyncResult *result, EHTMLEditor *editor) { - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; gchar *contents; gsize length; GError *error = NULL; @@ -103,9 +104,10 @@ insert_text_file_ready_cb (GFile *file, return; } - selection = e_html_editor_view_get_selection ( - e_html_editor_get_view (editor)); - e_html_editor_selection_insert_text (selection, contents); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_content ( + cnt_editor, contents, E_CONTENT_EDITOR_INSERT_TEXT_PLAIN); + g_free (contents); g_object_unref (editor); @@ -116,464 +118,172 @@ insert_text_file_ready_cb (GFile *file, *****************************************************************************/ static void -prepare_history_for_table (EHTMLEditor *editor, - WebKitDOMElement *table, - EHTMLEditorViewHistoryEvent *ev) -{ - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - ev->type = HISTORY_TABLE_DIALOG; - - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - ev->data.dom.from = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (table), TRUE); -} - -static void -save_history_for_table (EHTMLEditor *editor, - WebKitDOMElement *table, - EHTMLEditorViewHistoryEvent *ev) -{ - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - if (table) - ev->data.dom.to = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (table), TRUE); - else - ev->data.dom.to = NULL; - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); -} - -static void action_context_delete_cell_contents_cb (GtkAction *action, - EHTMLEditor *editor) + EHTMLEditor *editor) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMNode *node; - WebKitDOMElement *cell, *table; - - g_return_if_fail (editor->priv->table_cell != NULL); + EContentEditor *cnt_editor; - cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD"); - if (!cell) { - cell = e_html_editor_dom_node_find_parent_element ( - editor->priv->table_cell, "TH"); - } - g_return_if_fail (cell != NULL); - - table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE"); - g_return_if_fail (table != NULL); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - prepare_history_for_table (editor, table, ev); - - while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (cell)))) - remove_node (node); - - save_history_for_table (editor, table, ev); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_delete_cell_contents (cnt_editor); } static void action_context_delete_column_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMElement *cell, *table; - WebKitDOMHTMLCollection *rows; - gulong index, length, ii; - - g_return_if_fail (editor->priv->table_cell != NULL); - - /* Find TD in which the selection starts */ - cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD"); - if (!cell) { - cell = e_html_editor_dom_node_find_parent_element ( - editor->priv->table_cell, "TH"); - } - g_return_if_fail (cell != NULL); + EContentEditor *cnt_editor; - table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE"); - g_return_if_fail (table != NULL); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - prepare_history_for_table (editor, table, ev); - - rows = webkit_dom_html_table_element_get_rows ( - WEBKIT_DOM_HTML_TABLE_ELEMENT (table)); - length = webkit_dom_html_collection_get_length (rows); - - index = webkit_dom_html_table_cell_element_get_cell_index ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell)); - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *row; - - row = webkit_dom_html_collection_item (rows, ii); - - webkit_dom_html_table_row_element_delete_cell ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index, NULL); - g_object_unref (row); - } - g_object_unref (rows); - - save_history_for_table (editor, table, ev); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_delete_column (cnt_editor); } static void action_context_delete_row_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMElement *row, *table; - - g_return_if_fail (editor->priv->table_cell != NULL); - - row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR"); - g_return_if_fail (row != NULL); - - table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE"); - g_return_if_fail (table != NULL); + EContentEditor *cnt_editor; - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - prepare_history_for_table (editor, table, ev); - - remove_node (WEBKIT_DOM_NODE (row)); - - save_history_for_table (editor, table, ev); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_delete_row (cnt_editor); } static void action_context_delete_table_cb (GtkAction *action, EHTMLEditor *editor) { - WebKitDOMElement *table; - EHTMLEditorViewHistoryEvent *ev = NULL; - - g_return_if_fail (editor->priv->table_cell != NULL); - - table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE"); - g_return_if_fail (table != NULL); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - prepare_history_for_table (editor, table, ev); - - remove_node (WEBKIT_DOM_NODE (table)); + EContentEditor *cnt_editor; - save_history_for_table (editor, NULL, ev); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_delete_table (cnt_editor); } static void action_context_insert_column_after_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMElement *cell, *row, *table; - gulong index; - - g_return_if_fail (editor->priv->table_cell != NULL); - - cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD"); - if (!cell) { - cell = e_html_editor_dom_node_find_parent_element ( - editor->priv->table_cell, "TH"); - } - g_return_if_fail (cell != NULL); - - row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR"); - g_return_if_fail (row != NULL); - - table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE"); - g_return_if_fail (table != NULL); + EContentEditor *cnt_editor; - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - prepare_history_for_table (editor, table, ev); - - /* Get the first row in the table */ - row = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_get_first_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)))); - - index = webkit_dom_html_table_cell_element_get_cell_index ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell)); - - while (row) { - webkit_dom_html_table_row_element_insert_cell ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index + 1, NULL); - - row = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row))); - } - - save_history_for_table (editor, table, ev); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_column_after (cnt_editor); } static void action_context_insert_column_before_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMElement *cell, *row, *table; - gulong index; - - g_return_if_fail (editor->priv->table_cell != NULL); - - cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD"); - if (!cell) { - cell = e_html_editor_dom_node_find_parent_element ( - editor->priv->table_cell, "TH"); - } - g_return_if_fail (cell != NULL); - - row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR"); - g_return_if_fail (row != NULL); - - table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE"); - g_return_if_fail (table != NULL); + EContentEditor *cnt_editor; - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - prepare_history_for_table (editor, table, ev); - - /* Get the first row in the table */ - row = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_get_first_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)))); - - index = webkit_dom_html_table_cell_element_get_cell_index ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell)); - - while (row) { - webkit_dom_html_table_row_element_insert_cell ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index - 1, NULL); - - row = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row))); - } - - save_history_for_table (editor, table, ev); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_column_before (cnt_editor); } static void action_context_insert_row_above_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMElement *row, *table; - WebKitDOMHTMLCollection *cells; - WebKitDOMHTMLElement *new_row; - gulong index, cell_count, ii; - - g_return_if_fail (editor->priv->table_cell != NULL); - - row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR"); - g_return_if_fail (row != NULL); - - table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE"); - g_return_if_fail (table != NULL); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - prepare_history_for_table (editor, table, ev); - - index = webkit_dom_html_table_row_element_get_row_index ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + EContentEditor *cnt_editor; - new_row = webkit_dom_html_table_element_insert_row ( - WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index, NULL); - - cells = webkit_dom_html_table_row_element_get_cells ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); - cell_count = webkit_dom_html_collection_get_length (cells); - for (ii = 0; ii < cell_count; ii++) { - webkit_dom_html_table_row_element_insert_cell ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL); - } - g_object_unref (cells); - - save_history_for_table (editor, table, ev); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_row_above (cnt_editor); } static void action_context_insert_row_below_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMElement *row, *table; - WebKitDOMHTMLCollection *cells; - WebKitDOMHTMLElement *new_row; - gulong index, cell_count, ii; - - g_return_if_fail (editor->priv->table_cell != NULL); - - row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR"); - g_return_if_fail (row != NULL); - - table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE"); - g_return_if_fail (table != NULL); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - prepare_history_for_table (editor, table, ev); - - index = webkit_dom_html_table_row_element_get_row_index ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); - - new_row = webkit_dom_html_table_element_insert_row ( - WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index + 1, NULL); - - cells = webkit_dom_html_table_row_element_get_cells ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); - cell_count = webkit_dom_html_collection_get_length (cells); - for (ii = 0; ii < cell_count; ii++) { - webkit_dom_html_table_row_element_insert_cell ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL); - } - g_object_unref (cells); + EContentEditor *cnt_editor; - save_history_for_table (editor, table, ev); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_row_below (cnt_editor); } static void action_context_remove_link_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - e_html_editor_selection_unlink (selection); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_selection_unlink (cnt_editor); } static void action_context_spell_add_cb (GtkAction *action, EHTMLEditor *editor) { + EContentEditor *cnt_editor; ESpellChecker *spell_checker; - EHTMLEditorSelection *selection; gchar *word; - spell_checker = e_html_editor_view_get_spell_checker ( - editor->priv->html_editor_view); - selection = e_html_editor_view_get_selection (editor->priv->html_editor_view); - - word = e_html_editor_selection_get_caret_word (selection); - if (word && *word) { + cnt_editor = e_html_editor_get_content_editor (editor); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); + word = e_content_editor_get_caret_word (cnt_editor); + if (word && *word) e_spell_checker_learn_word (spell_checker, word); - } + g_free (word); + g_clear_object (&spell_checker); } static void action_context_spell_ignore_cb (GtkAction *action, EHTMLEditor *editor) { + EContentEditor *cnt_editor; ESpellChecker *spell_checker; - EHTMLEditorSelection *selection; gchar *word; - spell_checker = e_html_editor_view_get_spell_checker ( - editor->priv->html_editor_view); - selection = e_html_editor_view_get_selection (editor->priv->html_editor_view); - - word = e_html_editor_selection_get_caret_word (selection); - if (word && *word) { + cnt_editor = e_html_editor_get_content_editor (editor); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); + word = e_content_editor_get_caret_word (cnt_editor); + if (word && *word) e_spell_checker_ignore_word (spell_checker, word); - } + g_free (word); + g_clear_object (&spell_checker); } static void action_copy_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; - webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (view)); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_copy (cnt_editor); } static void action_cut_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (!webkit_dom_dom_selection_get_range_count (dom_selection) || - webkit_dom_dom_selection_get_is_collapsed (dom_selection)) { - g_object_unref (dom_selection); - return; - } - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - - selection = e_html_editor_view_get_selection (view); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_DELETE; + EContentEditor *cnt_editor; - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - - /* Save the fragment. */ - fragment = webkit_dom_range_clone_contents (range, NULL); - g_object_unref (range); - g_object_unref (dom_selection); - ev->data.fragment = g_object_ref (fragment); - - webkit_web_view_cut_clipboard (WEBKIT_WEB_VIEW (view)); - - ev->after.start.x = ev->before.start.x; - ev->after.start.y = ev->before.start.y; - ev->after.end.x = ev->before.start.x; - ev->after.end.y = ev->before.start.y; - - e_html_editor_view_insert_new_history_event (view, ev); - - e_html_editor_view_force_spell_check_for_current_paragraph (view); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_cut (cnt_editor); } static void action_indent_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; - if (gtk_widget_has_focus (GTK_WIDGET (view))) - e_html_editor_selection_indent (editor->priv->selection); + cnt_editor = e_html_editor_get_content_editor (editor); + if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) + e_content_editor_selection_indent (cnt_editor); } static void action_insert_emoticon_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; EEmoticon *emoticon; - emoticon = e_emoticon_chooser_get_current_emoticon ( - E_EMOTICON_CHOOSER (action)); + emoticon = e_emoticon_chooser_get_current_emoticon (E_EMOTICON_CHOOSER (action)); g_return_if_fail (emoticon != NULL); - view = e_html_editor_get_view (editor); - e_html_editor_view_insert_smiley (view, emoticon); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_emoticon (cnt_editor, emoticon); } static void @@ -620,15 +330,13 @@ action_insert_image_cb (GtkAction *action, dialog = e_image_chooser_dialog_new (C_("dialog-title", "Insert Image"), NULL); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; gchar *uri; uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_insert_image (selection, uri); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_image (cnt_editor, uri); g_free (uri); } @@ -644,8 +352,7 @@ action_insert_link_cb (GtkAction *action, editor->priv->link_dialog = e_html_editor_link_dialog_new (editor); - e_html_editor_link_dialog_show ( - E_HTML_EDITOR_LINK_DIALOG (editor->priv->link_dialog), NULL); + gtk_window_present (GTK_WINDOW (editor->priv->link_dialog)); } static void @@ -656,8 +363,7 @@ action_insert_rule_cb (GtkAction *action, editor->priv->hrule_dialog = e_html_editor_hrule_dialog_new (editor); - e_html_editor_hrule_dialog_show ( - E_HTML_EDITOR_HRULE_DIALOG (editor->priv->hrule_dialog), NULL); + gtk_window_present (GTK_WINDOW (editor->priv->hrule_dialog)); } static void @@ -710,19 +416,20 @@ static void action_language_cb (GtkToggleAction *toggle_action, EHTMLEditor *editor) { - ESpellChecker *checker; - EHTMLEditorView *view; + ESpellChecker *spell_checker; + EContentEditor *cnt_editor; const gchar *language_code; GtkAction *add_action; gchar *action_name; gboolean active; - view = e_html_editor_get_view (editor); - checker = e_html_editor_view_get_spell_checker (view); + cnt_editor = e_html_editor_get_content_editor (editor); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); language_code = gtk_action_get_name (GTK_ACTION (toggle_action)); active = gtk_toggle_action_get_active (toggle_action); - e_spell_checker_set_language_active (checker, language_code, active); + e_spell_checker_set_language_active (spell_checker, language_code, active); + g_clear_object (&spell_checker); /* Update "Add Word To" context menu item visibility. */ action_name = g_strdup_printf ("context-spell-add-%s", language_code); @@ -739,15 +446,15 @@ static gboolean update_mode_combobox (gpointer data) { EHTMLEditor *editor = data; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GtkAction *action; gboolean is_html; if (!E_IS_HTML_EDITOR (editor)) return FALSE; - view = e_html_editor_get_view (editor); - is_html = e_html_editor_view_get_html_mode (view); + cnt_editor = e_html_editor_get_content_editor (editor); + is_html = e_content_editor_get_html_mode (cnt_editor); action = gtk_action_group_get_action ( editor->priv->core_editor_actions, "mode-html"); @@ -762,13 +469,13 @@ action_mode_cb (GtkRadioAction *action, GtkRadioAction *current, EHTMLEditor *editor) { + EContentEditor *cnt_editor; GtkActionGroup *action_group; - EHTMLEditorView *view; GtkWidget *style_combo_box; gboolean is_html; - view = e_html_editor_get_view (editor); - is_html = e_html_editor_view_get_html_mode (view); + cnt_editor = e_html_editor_get_content_editor (editor); + is_html = e_content_editor_get_html_mode (cnt_editor); /* This must be done from idle callback, because apparently we can change * current value in callback of current value change */ @@ -776,7 +483,6 @@ action_mode_cb (GtkRadioAction *action, action_group = editor->priv->html_actions; gtk_action_group_set_visible (action_group, is_html); - gtk_action_group_set_sensitive (action_group, is_html); action_group = editor->priv->html_context_actions; gtk_action_group_set_visible (action_group, is_html); @@ -814,36 +520,111 @@ static void action_paste_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; - webkit_web_view_paste_clipboard (WEBKIT_WEB_VIEW (view)); - e_html_editor_view_force_spell_check_in_viewport (view); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_paste (cnt_editor); +} + +static void +clipboard_text_received_for_paste_as_text (GtkClipboard *clipboard, + const gchar *text, + EHTMLEditor *editor) +{ + EContentEditor *cnt_editor; + + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_content ( + cnt_editor, + text, + E_CONTENT_EDITOR_INSERT_CONVERT | + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN); } static void action_paste_as_text_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; + GtkClipboard *clipboard; + + cnt_editor = e_html_editor_get_content_editor (editor); + if (!gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) + gtk_widget_grab_focus (GTK_WIDGET (cnt_editor)); + + clipboard = gtk_clipboard_get_for_display ( + gdk_display_get_default (), + GDK_SELECTION_CLIPBOARD); + + gtk_clipboard_request_text ( + clipboard, + (GtkClipboardTextReceivedFunc) clipboard_text_received_for_paste_as_text, + editor); +} + +static void +paste_quote_text (EHTMLEditor *editor, + const gchar *text, + gboolean is_html) +{ + EContentEditor *cnt_editor; + + g_return_if_fail (E_IS_HTML_EDITOR (editor)); + g_return_if_fail (text != NULL); + + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_insert_content ( + cnt_editor, + text, + E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT | + (is_html ? E_CONTENT_EDITOR_INSERT_TEXT_HTML : E_CONTENT_EDITOR_INSERT_TEXT_PLAIN)); +} + +static void +clipboard_html_received_for_paste_quote (GtkClipboard *clipboard, + const gchar *text, + gpointer user_data) +{ + EHTMLEditor *editor = user_data; - if (!gtk_widget_has_focus (GTK_WIDGET (view))) - gtk_widget_grab_focus (GTK_WIDGET (view)); + g_return_if_fail (E_IS_HTML_EDITOR (editor)); + g_return_if_fail (text != NULL); - e_html_editor_view_paste_as_text (view); - e_html_editor_view_force_spell_check_in_viewport (view); + paste_quote_text (editor, text, TRUE); +} + +static void +clipboard_text_received_for_paste_quote (GtkClipboard *clipboard, + const gchar *text, + gpointer user_data) +{ + EHTMLEditor *editor = user_data; + + g_return_if_fail (E_IS_HTML_EDITOR (editor)); + g_return_if_fail (text != NULL); + + paste_quote_text (editor, text, FALSE); } static void action_paste_quote_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; + GtkClipboard *clipboard; - if (!gtk_widget_has_focus (GTK_WIDGET (view))) - gtk_widget_grab_focus (GTK_WIDGET (view)); + cnt_editor = e_html_editor_get_content_editor (editor); + if (!gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) + gtk_widget_grab_focus (GTK_WIDGET (cnt_editor)); - e_html_editor_view_paste_clipboard_quoted (view); - e_html_editor_view_force_spell_check_in_viewport (view); + clipboard = gtk_clipboard_get_for_display ( + gdk_display_get_default (), + GDK_SELECTION_CLIPBOARD); + + if (e_clipboard_wait_is_html_available (clipboard)) + e_clipboard_request_html (clipboard, clipboard_html_received_for_paste_quote, editor); + else if (gtk_clipboard_wait_is_text_available (clipboard)) + gtk_clipboard_request_text (clipboard, clipboard_text_received_for_paste_quote, editor); } static void @@ -855,9 +636,7 @@ action_properties_cell_cb (GtkAction *action, e_html_editor_cell_dialog_new (editor); } - e_html_editor_cell_dialog_show ( - E_HTML_EDITOR_CELL_DIALOG (editor->priv->cell_dialog), - editor->priv->table_cell); + gtk_window_present (GTK_WINDOW (editor->priv->cell_dialog)); } static void @@ -870,8 +649,7 @@ action_properties_image_cb (GtkAction *action, } e_html_editor_image_dialog_show ( - E_HTML_EDITOR_IMAGE_DIALOG (editor->priv->image_dialog), - editor->priv->image); + E_HTML_EDITOR_IMAGE_DIALOG (editor->priv->image_dialog)); } static void @@ -883,9 +661,7 @@ action_properties_link_cb (GtkAction *action, e_html_editor_link_dialog_new (editor); } - e_html_editor_link_dialog_show ( - E_HTML_EDITOR_LINK_DIALOG (editor->priv->link_dialog), - editor->priv->current_node); + gtk_window_present (GTK_WINDOW (editor->priv->link_dialog)); } static void @@ -921,9 +697,7 @@ action_properties_rule_cb (GtkAction *action, e_html_editor_hrule_dialog_new (editor); } - e_html_editor_hrule_dialog_show ( - E_HTML_EDITOR_HRULE_DIALOG (editor->priv->hrule_dialog), - editor->priv->current_node); + gtk_window_present (GTK_WINDOW (editor->priv->hrule_dialog)); } static void @@ -954,20 +728,22 @@ static void action_redo_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; - if (gtk_widget_has_focus (GTK_WIDGET (view))) - e_html_editor_view_redo (view); + cnt_editor = e_html_editor_get_content_editor (editor); + if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) + e_content_editor_redo (cnt_editor); } static void action_select_all_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; - if (gtk_widget_has_focus (GTK_WIDGET (view))) - webkit_web_view_select_all (WEBKIT_WEB_VIEW (view)); + cnt_editor = e_html_editor_get_content_editor (editor); + if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) + e_content_editor_select_all (cnt_editor); } static void @@ -1021,43 +797,34 @@ static void action_undo_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; - if (gtk_widget_has_focus (GTK_WIDGET (view))) - e_html_editor_view_undo (view); + cnt_editor = e_html_editor_get_content_editor (editor); + if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) { + e_content_editor_undo (cnt_editor); + } } static void action_unindent_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; - if (gtk_widget_has_focus (GTK_WIDGET (view))) - e_html_editor_selection_unindent (editor->priv->selection); + cnt_editor = e_html_editor_get_content_editor (editor); + if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) + e_content_editor_selection_unindent (cnt_editor); } static void action_wrap_lines_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view = e_html_editor_get_view (editor); + EContentEditor *cnt_editor; - if (gtk_widget_has_focus (GTK_WIDGET (view))) - e_html_editor_selection_wrap_lines (editor->priv->selection); -} - -static void -action_show_webkit_inspector_cb (GtkAction *action, - EHTMLEditor *editor) -{ - WebKitWebInspector *inspector; - EHTMLEditorView *view; - - view = e_html_editor_get_view (editor); - inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view)); - - webkit_web_inspector_show (inspector); + cnt_editor = e_html_editor_get_content_editor (editor); + if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) + e_content_editor_selection_wrap (cnt_editor); } /***************************************************************************** @@ -1239,14 +1006,7 @@ static GtkActionEntry core_editor_entries[] = { N_("_Wrap Lines"), "<Control>k", NULL, - G_CALLBACK (action_wrap_lines_cb) }, - - { "webkit-inspector", - NULL, - N_("Open Inspector"), - NULL, - NULL, - G_CALLBACK (action_show_webkit_inspector_cb) }, + G_CALLBACK (action_wrap_lines_cb) } }; static GtkRadioActionEntry core_justify_entries[] = { @@ -1256,21 +1016,21 @@ static GtkRadioActionEntry core_justify_entries[] = { N_("_Center"), "<Control>e", N_("Center Alignment"), - E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER }, + E_CONTENT_EDITOR_ALIGNMENT_CENTER }, { "justify-left", "format-justify-left", N_("_Left"), "<Control>l", N_("Left Alignment"), - E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT }, + E_CONTENT_EDITOR_ALIGNMENT_LEFT }, { "justify-right", "format-justify-right", N_("_Right"), "<Control>r", N_("Right Alignment"), - E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT } + E_CONTENT_EDITOR_ALIGNMENT_RIGHT } }; static GtkRadioActionEntry core_mode_entries[] = { @@ -1280,14 +1040,14 @@ static GtkRadioActionEntry core_mode_entries[] = { N_("_HTML"), NULL, N_("HTML editing mode"), - TRUE }, /* e_html_editor_view_set_html_mode */ + TRUE }, /* e_content_editor_set_html_mode */ { "mode-plain", NULL, N_("Plain _Text"), NULL, N_("Plain text editing mode"), - FALSE } /* e_html_editor_view_set_html_mode */ + FALSE } /* e_content_editor_set_html_mode */ }; static GtkRadioActionEntry core_style_entries[] = { @@ -1297,98 +1057,91 @@ static GtkRadioActionEntry core_style_entries[] = { N_("_Normal"), "<Control>0", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH }, + E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH }, { "style-h1", NULL, N_("Header _1"), "<Control>1", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1 }, + E_CONTENT_EDITOR_BLOCK_FORMAT_H1 }, { "style-h2", NULL, N_("Header _2"), "<Control>2", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2 }, + E_CONTENT_EDITOR_BLOCK_FORMAT_H2 }, { "style-h3", NULL, N_("Header _3"), "<Control>3", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3 }, + E_CONTENT_EDITOR_BLOCK_FORMAT_H3 }, { "style-h4", NULL, N_("Header _4"), "<Control>4", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4 }, + E_CONTENT_EDITOR_BLOCK_FORMAT_H4 }, { "style-h5", NULL, N_("Header _5"), "<Control>5", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5 }, + E_CONTENT_EDITOR_BLOCK_FORMAT_H5 }, { "style-h6", NULL, N_("Header _6"), "<Control>6", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6 }, + E_CONTENT_EDITOR_BLOCK_FORMAT_H6 }, { "style-preformat", NULL, N_("_Preformatted"), "<Control>7", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE }, + E_CONTENT_EDITOR_BLOCK_FORMAT_PRE }, { "style-address", NULL, N_("A_ddress"), "<Control>8", NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS }, - - { "style-blockquote", - NULL, - N_("_Blockquote"), - "<Control>9", - NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE }, + E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS }, { "style-list-bullet", NULL, N_("_Bulleted List"), NULL, NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST }, + E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST }, { "style-list-roman", NULL, N_("_Roman Numeral List"), NULL, NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN }, + E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN }, { "style-list-number", NULL, N_("Numbered _List"), NULL, NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST }, + E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST }, { "style-list-alpha", NULL, N_("_Alphabetical List"), NULL, NULL, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA } + E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA } }; /***************************************************************************** @@ -1549,7 +1302,7 @@ static GtkRadioActionEntry html_size_entries[] = { N_("-2"), NULL, NULL, - E_HTML_EDITOR_SELECTION_FONT_SIZE_TINY }, + E_CONTENT_EDITOR_FONT_SIZE_TINY }, { "size-minus-one", NULL, @@ -1557,7 +1310,7 @@ static GtkRadioActionEntry html_size_entries[] = { N_("-1"), NULL, NULL, - E_HTML_EDITOR_SELECTION_FONT_SIZE_SMALL }, + E_CONTENT_EDITOR_FONT_SIZE_SMALL }, { "size-plus-zero", NULL, @@ -1565,7 +1318,7 @@ static GtkRadioActionEntry html_size_entries[] = { N_("+0"), NULL, NULL, - E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL }, + E_CONTENT_EDITOR_FONT_SIZE_NORMAL }, { "size-plus-one", NULL, @@ -1573,7 +1326,7 @@ static GtkRadioActionEntry html_size_entries[] = { N_("+1"), NULL, NULL, - E_HTML_EDITOR_SELECTION_FONT_SIZE_BIG }, + E_CONTENT_EDITOR_FONT_SIZE_BIG }, { "size-plus-two", NULL, @@ -1581,7 +1334,7 @@ static GtkRadioActionEntry html_size_entries[] = { N_("+2"), NULL, NULL, - E_HTML_EDITOR_SELECTION_FONT_SIZE_BIGGER }, + E_CONTENT_EDITOR_FONT_SIZE_BIGGER }, { "size-plus-three", NULL, @@ -1589,7 +1342,7 @@ static GtkRadioActionEntry html_size_entries[] = { N_("+3"), NULL, NULL, - E_HTML_EDITOR_SELECTION_FONT_SIZE_LARGE }, + E_CONTENT_EDITOR_FONT_SIZE_LARGE }, { "size-plus-four", NULL, @@ -1597,7 +1350,7 @@ static GtkRadioActionEntry html_size_entries[] = { N_("+4"), NULL, NULL, - E_HTML_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE } + E_CONTENT_EDITOR_FONT_SIZE_VERY_LARGE } }; /***************************************************************************** @@ -1649,13 +1402,6 @@ static GtkActionEntry context_entries[] = { NULL, NULL }, - { "context-input-methods-menu", - NULL, - N_("Input Methods"), - NULL, - NULL, - NULL }, - { "context-insert-table-menu", NULL, /* Translators: Popup menu item caption, containing all the Insert options for a table */ @@ -1717,13 +1463,6 @@ static GtkActionEntry html_context_entries[] = { NULL, G_CALLBACK (action_context_insert_row_below_cb) }, - { "context-insert-table", - NULL, - N_("Table"), - NULL, - NULL, - G_CALLBACK (action_insert_table_cb) }, - { "context-properties-cell", NULL, N_("Cell..."), @@ -1832,27 +1571,27 @@ static GtkActionEntry spell_context_entries[] = { static void editor_actions_setup_languages_menu (EHTMLEditor *editor) { - ESpellChecker *checker; - EHTMLEditorView *view; + ESpellChecker *spell_checker; + EContentEditor *cnt_editor; GtkUIManager *manager; GtkActionGroup *action_group; - GList *list, *link; + GList *list = NULL, *link; guint merge_id; manager = editor->priv->manager; action_group = editor->priv->language_actions; - view = e_html_editor_get_view (editor); - checker = e_html_editor_view_get_spell_checker (view); + cnt_editor = e_html_editor_get_content_editor (editor); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); merge_id = gtk_ui_manager_new_merge_id (manager); - list = e_spell_checker_list_available_dicts (checker); + list = e_spell_checker_list_available_dicts (spell_checker); for (link = list; link != NULL; link = g_list_next (link)) { ESpellDictionary *dictionary = link->data; GtkToggleAction *action; const gchar *language_name; GString *escaped_name = NULL; - gboolean active; + gboolean active = FALSE; language_name = e_spell_dictionary_get_name (dictionary); if (language_name && strchr (language_name, '_') != NULL) @@ -1870,7 +1609,7 @@ editor_actions_setup_languages_menu (EHTMLEditor *editor) * We're not prepared to invoke the signal handler yet. * The "Add Word To" actions have not yet been added. */ active = e_spell_checker_get_language_active ( - checker, e_spell_dictionary_get_code (dictionary)); + spell_checker, e_spell_dictionary_get_code (dictionary)); gtk_toggle_action_set_active (action, active); g_signal_connect ( @@ -1891,21 +1630,24 @@ editor_actions_setup_languages_menu (EHTMLEditor *editor) } g_list_free (list); + g_clear_object (&spell_checker); } static void editor_actions_setup_spell_check_menu (EHTMLEditor *editor) { - ESpellChecker *checker; + EContentEditor *cnt_editor; + ESpellChecker *spell_checker; GtkUIManager *manager; GtkActionGroup *action_group; - GList *available_dicts, *iter; + GList *available_dicts = NULL, *iter; guint merge_id; manager = editor->priv->manager; action_group = editor->priv->spell_check_actions;; - checker = e_html_editor_view_get_spell_checker (editor->priv->html_editor_view); - available_dicts = e_spell_checker_list_available_dicts (checker); + cnt_editor = e_html_editor_get_content_editor (editor); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); + available_dicts = e_spell_checker_list_available_dicts (spell_checker); merge_id = gtk_ui_manager_new_merge_id (manager); for (iter = available_dicts; iter; iter = iter->next) { @@ -1955,7 +1697,7 @@ editor_actions_setup_spell_check_menu (EHTMLEditor *editor) /* Visibility is dependent on whether the * corresponding language action is active. */ - gtk_action_set_visible (action, e_spell_checker_get_language_active (checker, code)); + gtk_action_set_visible (action, FALSE); gtk_action_group_add_action (action_group, action); @@ -1975,6 +1717,7 @@ editor_actions_setup_spell_check_menu (EHTMLEditor *editor) } g_list_free (available_dicts); + g_clear_object (&spell_checker); } void @@ -1984,14 +1727,11 @@ editor_actions_init (EHTMLEditor *editor) GtkActionGroup *action_group; GtkUIManager *manager; const gchar *domain; - EHTMLEditorView *view; - GSettings *settings; g_return_if_fail (E_IS_HTML_EDITOR (editor)); manager = e_html_editor_get_ui_manager (editor); domain = GETTEXT_PACKAGE; - view = e_html_editor_get_view (editor); /* Core Actions */ action_group = editor->priv->core_actions; @@ -2009,7 +1749,7 @@ editor_actions_init (EHTMLEditor *editor) gtk_action_group_add_radio_actions ( action_group, core_justify_entries, G_N_ELEMENTS (core_justify_entries), - E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT, + E_CONTENT_EDITOR_ALIGNMENT_LEFT, NULL, NULL); gtk_action_group_add_radio_actions ( action_group, core_mode_entries, @@ -2019,19 +1759,10 @@ editor_actions_init (EHTMLEditor *editor) gtk_action_group_add_radio_actions ( action_group, core_style_entries, G_N_ELEMENTS (core_style_entries), - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH, + E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH, NULL, NULL); gtk_ui_manager_insert_action_group (manager, action_group, 0); - action = gtk_action_group_get_action (action_group, "mode-html"); - e_binding_bind_property ( - view, "html-mode", - action, "current-value", - G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); - - /* Synchronize wiget mode with the buttons */ - e_html_editor_view_set_html_mode (view, TRUE); - /* Face Action */ action = e_emoticon_action_new ( "insert-emoticon", _("_Emoticon"), @@ -2055,7 +1786,7 @@ editor_actions_init (EHTMLEditor *editor) gtk_action_group_add_radio_actions ( action_group, html_size_entries, G_N_ELEMENTS (html_size_entries), - E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL, + E_CONTENT_EDITOR_FONT_SIZE_NORMAL, NULL, NULL); gtk_ui_manager_insert_action_group (manager, action_group, 0); @@ -2120,92 +1851,113 @@ editor_actions_init (EHTMLEditor *editor) gtk_action_set_sensitive (ACTION (UNINDENT), FALSE); gtk_action_set_sensitive (ACTION (FIND_AGAIN), FALSE); +} + +void +editor_actions_bind (EHTMLEditor *editor) +{ + GtkAction *action; + GtkActionGroup *action_group; + EContentEditor *cnt_editor; + g_return_if_fail (E_IS_HTML_EDITOR (editor)); + + cnt_editor = e_html_editor_get_content_editor (editor); + + action_group = editor->priv->core_editor_actions; + action = gtk_action_group_get_action (action_group, "mode-html"); e_binding_bind_property ( - view, "can-redo", + cnt_editor, "html-mode", + action, "current-value", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + /* Synchronize widget mode with the buttons */ + e_content_editor_set_html_mode (cnt_editor, TRUE); + + e_binding_bind_property ( + cnt_editor, "can-redo", ACTION (REDO), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "can-undo", + cnt_editor, "can-undo", ACTION (UNDO), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "can-copy", + cnt_editor, "can-copy", ACTION (COPY), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "can-cut", + cnt_editor, "can-cut", ACTION (CUT), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "can-paste", + cnt_editor, "can-paste", ACTION (PASTE), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "can-paste", + cnt_editor, "can-paste", ACTION (PASTE_QUOTE), "sensitive", G_BINDING_SYNC_CREATE); /* This is connected to JUSTIFY_LEFT action only, but * it automatically applies on all actions in the group. */ e_binding_bind_property ( - editor->priv->selection, "alignment", + cnt_editor, "alignment", ACTION (JUSTIFY_LEFT), "current-value", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); e_binding_bind_property ( - editor->priv->selection, "bold", + cnt_editor, "bold", ACTION (BOLD), "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); e_binding_bind_property ( - editor->priv->selection, "font-size", + cnt_editor, "font-size", ACTION (FONT_SIZE_GROUP), "current-value", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); e_binding_bind_property ( - editor->priv->selection, "block-format", + cnt_editor, "block-format", ACTION (STYLE_NORMAL), "current-value", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); e_binding_bind_property ( - editor->priv->selection, "indented", + cnt_editor, "indented", ACTION (UNINDENT), "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - editor->priv->selection, "italic", + cnt_editor, "italic", ACTION (ITALIC), "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); e_binding_bind_property ( - editor->priv->selection, "monospaced", + cnt_editor, "monospaced", ACTION (MONOSPACED), "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); e_binding_bind_property ( - editor->priv->selection, "strikethrough", + cnt_editor, "strikethrough", ACTION (STRIKETHROUGH), "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); e_binding_bind_property ( - editor->priv->selection, "underline", + cnt_editor, "underline", ACTION (UNDERLINE), "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + e_binding_bind_property ( + cnt_editor, "html-mode", + editor->priv->html_actions, "sensitive", + G_BINDING_SYNC_CREATE); + /* Disable all actions and toolbars when editor is not editable */ e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", editor->priv->core_editor_actions, "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", editor->priv->html_actions, "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", editor->priv->spell_check_actions, "sensitive", G_BINDING_SYNC_CREATE); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", editor->priv->suggestion_actions, "sensitive", G_BINDING_SYNC_CREATE); - - settings = e_util_ref_settings ("org.gnome.evolution.mail"); - gtk_action_set_visible ( - ACTION (WEBKIT_INSPECTOR), - g_settings_get_boolean (settings, "composer-developer-mode")); - g_object_unref (settings); } diff --git a/e-util/e-html-editor-actions.h b/e-util/e-html-editor-actions.h index 6991503..f724db0 100644 --- a/e-util/e-html-editor-actions.h +++ b/e-util/e-html-editor-actions.h @@ -47,8 +47,6 @@ E_HTML_EDITOR_ACTION ((editor), "context-insert-row-above") #define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_ROW_BELOW(editor) \ E_HTML_EDITOR_ACTION ((editor), "context-insert-row-below") -#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_TABLE(editor) \ - E_HTML_EDITOR_ACTION ((editor), "context-insert-table") #define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_CELL(editor) \ E_HTML_EDITOR_ACTION ((editor), "context-properties-cell") #define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_IMAGE(editor) \ @@ -85,6 +83,8 @@ E_HTML_EDITOR_ACTION ((editor), "format-menu") #define E_HTML_EDITOR_ACTION_FORMAT_TEXT(editor) \ E_HTML_EDITOR_ACTION ((editor), "format-text") +#define E_HTML_EDITOR_ACTION_INSERT_EMOTICON(editor) \ + E_HTML_EDITOR_ACTION ((editor), "insert-emoticon") #define E_HTML_EDITOR_ACTION_INSERT_IMAGE(editor) \ E_HTML_EDITOR_ACTION ((editor), "insert-image") #define E_HTML_EDITOR_ACTION_INSERT_LINK(editor) \ @@ -157,7 +157,7 @@ E_HTML_EDITOR_ACTION ((editor), "undo") #define E_HTML_EDITOR_ACTION_UNINDENT(editor) \ E_HTML_EDITOR_ACTION ((editor), "unindent") -#define E_HTML_EDITOR_ACTION_WEBKIT_INSPECTOR(editor) \ - E_HTML_EDITOR_ACTION ((editor), "webkit-inspector") +#define E_HTML_EDITOR_ACTION_WRAP_LINES(editor) \ + E_HTML_EDITOR_ACTION ((editor), "wrap-lines") #endif /* E_HTML_EDITOR_ACTIONS_H */ diff --git a/e-util/e-html-editor-cell-dialog.c b/e-util/e-html-editor-cell-dialog.c index 0ee0da2..b5379ec 100644 --- a/e-util/e-html-editor-cell-dialog.c +++ b/e-util/e-html-editor-cell-dialog.c @@ -29,10 +29,8 @@ #include "e-color-combo.h" #include "e-dialog-widgets.h" -#include "e-html-editor-utils.h" #include "e-image-chooser-dialog.h" #include "e-misc-utils.h" -#include "e-misc-utils.h" #define E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -62,10 +60,7 @@ struct _EHTMLEditorCellDialogPrivate { GtkWidget *remove_image_button; - WebKitDOMElement *cell; guint scope; - - EHTMLEditorViewHistoryEvent *history_event; }; enum { @@ -77,151 +72,12 @@ enum { static GdkRGBA transparent = { 0, 0, 0, 0 }; -typedef void (*DOMStrFunc) (WebKitDOMHTMLTableCellElement *cell, const gchar *val, gpointer user_data); -typedef void (*DOMUlongFunc) (WebKitDOMHTMLTableCellElement *cell, gulong val, gpointer user_data); -typedef void (*DOMBoolFunc) (WebKitDOMHTMLTableCellElement *cell, gboolean val, gpointer user_data); - G_DEFINE_TYPE ( EHTMLEditorCellDialog, e_html_editor_cell_dialog, E_TYPE_HTML_EDITOR_DIALOG); static void -call_cell_dom_func (WebKitDOMHTMLTableCellElement *cell, - gpointer func, - GValue *value, - gpointer user_data) -{ - if (G_VALUE_HOLDS_STRING (value)) { - DOMStrFunc f = func; - f (cell, g_value_get_string (value), user_data); - } else if (G_VALUE_HOLDS_ULONG (value)) { - DOMUlongFunc f = func; - f (cell, g_value_get_ulong (value), user_data); - } else if (G_VALUE_HOLDS_BOOLEAN (value)) { - DOMBoolFunc f = func; - f (cell, g_value_get_boolean (value), user_data); - } -} - -static void -for_each_cell_do (WebKitDOMElement *row, - gpointer func, - GValue *value, - gpointer user_data) -{ - WebKitDOMHTMLCollection *cells; - gulong ii, length; - cells = webkit_dom_html_table_row_element_get_cells ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); - length = webkit_dom_html_collection_get_length (cells); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *cell; - cell = webkit_dom_html_collection_item (cells, ii); - if (!cell) { - continue; - } - - call_cell_dom_func ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), func, value, user_data); - g_object_unref (cell); - } - g_object_unref (cells); -} - -static void -html_editor_cell_dialog_set_attribute (EHTMLEditorCellDialog *dialog, - gpointer func, - GValue *value, - gpointer user_data) -{ - if (dialog->priv->scope == SCOPE_CELL) { - - call_cell_dom_func ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell), - func, value, user_data); - - } else if (dialog->priv->scope == SCOPE_COLUMN) { - gulong index, ii, length; - WebKitDOMElement *table; - WebKitDOMHTMLCollection *rows; - - index = webkit_dom_html_table_cell_element_get_cell_index ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)); - table = e_html_editor_dom_node_find_parent_element ( - WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE"); - if (!table) { - return; - } - - rows = webkit_dom_html_table_element_get_rows ( - WEBKIT_DOM_HTML_TABLE_ELEMENT (table)); - length = webkit_dom_html_collection_get_length (rows); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *row, *cell; - WebKitDOMHTMLCollection *cells; - - row = webkit_dom_html_collection_item (rows, ii); - cells = webkit_dom_html_table_row_element_get_cells ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); - cell = webkit_dom_html_collection_item (cells, index); - if (!cell) { - g_object_unref (row); - g_object_unref (cells); - continue; - } - - call_cell_dom_func ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), - func, value, user_data); - g_object_unref (row); - g_object_unref (cells); - g_object_unref (cell); - } - g_object_unref (rows); - - } else if (dialog->priv->scope == SCOPE_ROW) { - WebKitDOMElement *row; - - row = e_html_editor_dom_node_find_parent_element ( - WEBKIT_DOM_NODE (dialog->priv->cell), "TR"); - if (!row) { - return; - } - - for_each_cell_do (row, func, value, user_data); - - } else if (dialog->priv->scope == SCOPE_TABLE) { - gulong ii, length; - WebKitDOMElement *table; - WebKitDOMHTMLCollection *rows; - - table = e_html_editor_dom_node_find_parent_element ( - WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE"); - if (!table) { - return; - } - - rows = webkit_dom_html_table_element_get_rows ( - WEBKIT_DOM_HTML_TABLE_ELEMENT (table)); - length = webkit_dom_html_collection_get_length (rows); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *row; - - row = webkit_dom_html_collection_item (rows, ii); - if (!row) { - continue; - } - - for_each_cell_do ( - WEBKIT_DOM_ELEMENT (row), func, value, user_data); - g_object_unref (row); - } - g_object_unref (rows); - } -} - -static void html_editor_cell_dialog_set_scope (EHTMLEditorCellDialog *dialog) { if (gtk_toggle_button_get_active ( @@ -250,249 +106,184 @@ html_editor_cell_dialog_set_scope (EHTMLEditorCellDialog *dialog) static void html_editor_cell_dialog_set_valign (EHTMLEditorCellDialog *dialog) { - GValue val = { 0 }; - - g_value_init (&val, G_TYPE_STRING); - g_value_set_string ( - &val, - gtk_combo_box_get_active_id ( - GTK_COMBO_BOX (dialog->priv->valign_combo))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - html_editor_cell_dialog_set_attribute ( - dialog, webkit_dom_html_table_cell_element_set_v_align, &val, NULL); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_value_unset (&val); + e_content_editor_cell_set_v_align ( + cnt_editor, + gtk_combo_box_get_active_id ( + GTK_COMBO_BOX (dialog->priv->valign_combo)), + dialog->priv->scope); } static void html_editor_cell_dialog_set_halign (EHTMLEditorCellDialog *dialog) { - GValue val = { 0 }; - - g_value_init (&val, G_TYPE_STRING); - g_value_set_string ( - &val, - gtk_combo_box_get_active_id ( - GTK_COMBO_BOX (dialog->priv->halign_combo))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - html_editor_cell_dialog_set_attribute ( - dialog, webkit_dom_html_table_cell_element_set_align, &val, NULL); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_value_unset (&val); + e_content_editor_cell_set_align ( + cnt_editor, + gtk_combo_box_get_active_id ( + GTK_COMBO_BOX (dialog->priv->halign_combo)), + dialog->priv->scope); } static void html_editor_cell_dialog_set_wrap_text (EHTMLEditorCellDialog *dialog) { - GValue val = { 0 }; - - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean ( - &val, - !gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check))); - - html_editor_cell_dialog_set_attribute ( - dialog, webkit_dom_html_table_cell_element_set_no_wrap, &val, NULL); -} - -static void -cell_set_header_style (WebKitDOMHTMLTableCellElement *cell, - gboolean header_style, - gpointer user_data) -{ - EHTMLEditorCellDialog *dialog = user_data; - WebKitDOMDocument *document; - WebKitDOMNodeList *nodes; - WebKitDOMElement *new_cell; - gulong length, ii; - gchar *tagname; - - document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (cell)); - tagname = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (cell)); - - if (header_style && (g_ascii_strncasecmp (tagname, "TD", 2) == 0)) { - - new_cell = webkit_dom_document_create_element (document, "TH", NULL); - - } else if (!header_style && (g_ascii_strncasecmp (tagname, "TH", 2) == 0)) { - - new_cell = webkit_dom_document_create_element (document, "TD", NULL); - - } else { - g_free (tagname); - return; - } - - /* Move all child nodes from cell to new_cell */ - nodes = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (cell)); - length = webkit_dom_node_list_get_length (nodes); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (nodes, ii); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (new_cell), node, NULL); - g_object_unref (node); - } - g_object_unref (nodes); - - /* Insert new_cell before cell and remove cell */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (cell)), - WEBKIT_DOM_NODE (new_cell), - WEBKIT_DOM_NODE (cell), NULL); - - webkit_dom_node_remove_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (cell)), - WEBKIT_DOM_NODE (cell), NULL); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - dialog->priv->cell = new_cell; + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_free (tagname); + e_content_editor_cell_set_wrap ( + cnt_editor, + gtk_toggle_button_get_active ( + GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check)), + dialog->priv->scope); } static void html_editor_cell_dialog_set_header_style (EHTMLEditorCellDialog *dialog) { - GValue val = { 0 }; + EHTMLEditor *editor; + EContentEditor *cnt_editor; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean ( - &val, - gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (dialog->priv->header_style_check))); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - html_editor_cell_dialog_set_attribute ( - dialog, cell_set_header_style, &val, dialog); + e_content_editor_cell_set_header_style ( + cnt_editor, + gtk_toggle_button_get_active ( + GTK_TOGGLE_BUTTON (dialog->priv->header_style_check)), + dialog->priv->scope); } static void html_editor_cell_dialog_set_width (EHTMLEditorCellDialog *dialog) { - GValue val = { 0 }; - gchar *width; + EHTMLEditor *editor; + EContentEditor *cnt_editor; - if (!gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (dialog->priv->width_check))) { + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - width = g_strdup ("auto"); - } else { + if (gtk_toggle_button_get_active ( + GTK_TOGGLE_BUTTON (dialog->priv->width_check))) { - width = g_strdup_printf ( - "%d%s", + e_content_editor_cell_set_width ( + cnt_editor, gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON (dialog->priv->width_edit)), - ((gtk_combo_box_get_active ( + (gtk_combo_box_get_active ( GTK_COMBO_BOX (dialog->priv->width_units)) == 0) ? - "px" : "%")); - } + E_CONTENT_EDITOR_UNIT_PIXEL : + E_CONTENT_EDITOR_UNIT_PERCENTAGE, + dialog->priv->scope); + } else + e_content_editor_cell_set_width ( + cnt_editor, 0, E_CONTENT_EDITOR_UNIT_AUTO, dialog->priv->scope); +} - g_value_init (&val, G_TYPE_STRING); - g_value_take_string (&val, width); - html_editor_cell_dialog_set_attribute ( - dialog, webkit_dom_html_table_cell_element_set_width, &val, NULL); +static void +html_editor_cell_dialog_width_units_changed (GtkWidget *widget, + EHTMLEditorCellDialog *dialog) +{ + if (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->width_units)) == 0) { + gtk_spin_button_set_range ( + GTK_SPIN_BUTTON (dialog->priv->width_edit), 0, G_MAXUINT); + } else + gtk_spin_button_set_range ( + GTK_SPIN_BUTTON (dialog->priv->width_edit), 0, 100); - g_free (width); + html_editor_cell_dialog_set_width (dialog); } static void html_editor_cell_dialog_set_column_span (EHTMLEditorCellDialog *dialog) { - GValue val = { 0 }; + EHTMLEditor *editor; + EContentEditor *cnt_editor; - g_value_init (&val, G_TYPE_ULONG); - g_value_set_ulong ( - &val, - gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->col_span_edit))); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - html_editor_cell_dialog_set_attribute ( - dialog, webkit_dom_html_table_cell_element_set_col_span, &val, NULL); + e_content_editor_cell_set_col_span ( + cnt_editor, + gtk_spin_button_get_value_as_int ( + GTK_SPIN_BUTTON (dialog->priv->col_span_edit)), + dialog->priv->scope); } static void html_editor_cell_dialog_set_row_span (EHTMLEditorCellDialog *dialog) { - GValue val = { 0 }; + EHTMLEditor *editor; + EContentEditor *cnt_editor; - g_value_init (&val, G_TYPE_ULONG); - g_value_set_ulong ( - &val, - gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->row_span_edit))); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - html_editor_cell_dialog_set_attribute ( - dialog, webkit_dom_html_table_cell_element_set_row_span, &val, NULL); + e_content_editor_cell_set_row_span ( + cnt_editor, + gtk_spin_button_get_value_as_int ( + GTK_SPIN_BUTTON (dialog->priv->row_span_edit)), + dialog->priv->scope); } static void html_editor_cell_dialog_set_background_color (EHTMLEditorCellDialog *dialog) { - gchar *color = NULL; + EHTMLEditor *editor; + EContentEditor *cnt_editor; GdkRGBA rgba; - GValue val = { 0 }; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); e_color_combo_get_current_color ( E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba); - if (rgba.alpha != 0.0) - color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba)); - else - color = g_strdup (""); - - g_value_init (&val, G_TYPE_STRING); - g_value_take_string (&val, color); - - html_editor_cell_dialog_set_attribute ( - dialog, webkit_dom_html_table_cell_element_set_bg_color, &val, NULL); - - g_free (color); + e_content_editor_cell_set_background_color (cnt_editor, &rgba, dialog->priv->scope); } static void -cell_set_background_image (WebKitDOMHTMLTableCellElement *cell, - const gchar *uri, - EHTMLEditorCellDialog *dialog) +html_editor_cell_dialog_set_background_image (EHTMLEditorCellDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; + gchar *uri; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - - if (uri && *uri) { - e_html_editor_selection_replace_image_src ( - e_html_editor_view_get_selection (view), - WEBKIT_DOM_ELEMENT (cell), - uri); - } else - remove_image_attributes_from_element (WEBKIT_DOM_ELEMENT (cell)); - - gtk_widget_set_sensitive (dialog->priv->remove_image_button, uri && *uri); -} - -static void -html_editor_cell_dialog_set_background_image (EHTMLEditorCellDialog *dialog) -{ - gchar *uri; - GValue val = { 0 }; + cnt_editor = e_html_editor_get_content_editor (editor); uri = gtk_file_chooser_get_uri ( GTK_FILE_CHOOSER (dialog->priv->background_image_chooser)); - g_value_init (&val, G_TYPE_STRING); - g_value_take_string (&val, uri); + e_content_editor_cell_set_background_image_uri (cnt_editor, uri); + + gtk_widget_set_sensitive (dialog->priv->remove_image_button, uri && *uri); - html_editor_cell_dialog_set_attribute ( - dialog, cell_set_background_image, &val, dialog); + g_free (uri); } static void html_editor_cell_dialog_remove_image (EHTMLEditorCellDialog *dialog) { - remove_image_attributes_from_element ( - WEBKIT_DOM_ELEMENT (dialog->priv->cell)); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_cell_set_background_image_uri (cnt_editor, NULL); gtk_file_chooser_unselect_all ( GTK_FILE_CHOOSER (dialog->priv->background_image_chooser)); @@ -504,123 +295,72 @@ static void html_editor_cell_dialog_show (GtkWidget *widget) { EHTMLEditor *editor; + EContentEditor *cnt_editor; + EContentEditorUnit unit; EHTMLEditorCellDialog *dialog; - EHTMLEditorView *view; - gchar *tmp; - GdkRGBA color; + GdkRGBA rgba; + gchar *alignment, *uri; + gint width; dialog = E_HTML_EDITOR_CELL_DIALOG (widget); editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - EHTMLEditorViewHistoryEvent *ev; - WebKitDOMElement *table; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_TABLE_DIALOG; - - e_html_editor_selection_get_selection_coordinates ( - e_html_editor_view_get_selection (view), - &ev->before.start.x, &ev->before.start.y, - &ev->before.end.x, &ev->before.end.y); - - table = e_html_editor_dom_node_find_parent_element ( - WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE"); - ev->data.dom.from = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (table), TRUE); - dialog->priv->history_event = ev; - } + e_content_editor_on_cell_dialog_open (cnt_editor); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button), TRUE); - tmp = webkit_dom_html_table_cell_element_get_align ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)); + alignment = e_content_editor_cell_get_align (cnt_editor); gtk_combo_box_set_active_id ( GTK_COMBO_BOX (dialog->priv->halign_combo), - (tmp && *tmp) ? tmp : "left"); - g_free (tmp); + (alignment && *alignment) ? alignment : "left"); + g_free (alignment); - tmp = webkit_dom_html_table_cell_element_get_v_align ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)); + alignment = e_content_editor_cell_get_v_align (cnt_editor); gtk_combo_box_set_active_id ( GTK_COMBO_BOX (dialog->priv->valign_combo), - (tmp && *tmp) ? tmp : "middle"); - g_free (tmp); + (alignment && *alignment) ? alignment : "middle"); + g_free (alignment); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check), - !webkit_dom_html_table_cell_element_get_no_wrap ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell))); + e_content_editor_cell_get_wrap (cnt_editor)); - tmp = webkit_dom_element_get_tag_name ( - WEBKIT_DOM_ELEMENT (dialog->priv->cell)); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON (dialog->priv->header_style_check), - (g_ascii_strncasecmp (tmp, "TH", 2) == 0)); - g_free (tmp); - - tmp = webkit_dom_html_table_cell_element_get_width ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)); - if (tmp && *tmp) { - gint val = atoi (tmp); - gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->width_edit), val); - gtk_toggle_button_set_active ( - GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE); - } else { - gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->width_edit), 0); - gtk_toggle_button_set_active ( - GTK_TOGGLE_BUTTON (dialog->priv->width_check), FALSE); - } + e_content_editor_cell_is_header (cnt_editor)); + + width = e_content_editor_cell_get_width (cnt_editor, &unit); + gtk_spin_button_set_value ( + GTK_SPIN_BUTTON (dialog->priv->width_edit), width); + gtk_toggle_button_set_active ( + GTK_TOGGLE_BUTTON (dialog->priv->width_check), + unit != E_CONTENT_EDITOR_UNIT_AUTO); gtk_combo_box_set_active_id ( - GTK_COMBO_BOX (dialog->priv->width_units), "units-px"); - g_free (tmp); + GTK_COMBO_BOX (dialog->priv->width_units), + (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "units-px" : "units-percent"); gtk_spin_button_set_value ( GTK_SPIN_BUTTON (dialog->priv->row_span_edit), - webkit_dom_html_table_cell_element_get_row_span ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell))); + e_content_editor_cell_get_row_span (cnt_editor)); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON (dialog->priv->col_span_edit), - webkit_dom_html_table_cell_element_get_col_span ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell))); - - if (webkit_dom_element_has_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->cell), "background")) { - tmp = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->cell), "data-uri"); + e_content_editor_cell_get_col_span (cnt_editor)); + uri = e_content_editor_cell_get_background_image_uri (cnt_editor); + if (uri && *uri) gtk_file_chooser_set_uri ( - GTK_FILE_CHOOSER (dialog->priv->background_image_chooser), - tmp); - - g_free (tmp); - } else { + GTK_FILE_CHOOSER (dialog->priv->background_image_chooser), uri); + else gtk_file_chooser_unselect_all ( GTK_FILE_CHOOSER (dialog->priv->background_image_chooser)); - } + g_free (uri); - tmp = webkit_dom_html_table_cell_element_get_bg_color ( - WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)); - if (tmp && *tmp) { - if (gdk_rgba_parse (&color, tmp)) { - e_color_combo_set_current_color ( - E_COLOR_COMBO (dialog->priv->background_color_picker), - &color); - } else { - e_color_combo_set_current_color ( - E_COLOR_COMBO (dialog->priv->background_color_picker), - &transparent); - } - } else { - e_color_combo_set_current_color ( - E_COLOR_COMBO (dialog->priv->background_color_picker), - &transparent); - } - g_free (tmp); + e_content_editor_cell_get_background_color (cnt_editor, &rgba); + e_color_combo_set_current_color ( + E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba); GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->show (widget); } @@ -628,43 +368,15 @@ html_editor_cell_dialog_show (GtkWidget *widget) static void html_editor_cell_dialog_hide (GtkWidget *widget) { - EHTMLEditorCellDialogPrivate *priv; - EHTMLEditorViewHistoryEvent *ev; - - priv = E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE (widget); - ev = priv->history_event; - - if (ev) { - EHTMLEditorCellDialog *dialog; - EHTMLEditor *editor; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - WebKitDOMElement *table; - - dialog = E_HTML_EDITOR_CELL_DIALOG (widget); - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - table = e_html_editor_dom_node_find_parent_element ( - WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE"); - - ev->data.dom.to = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (table), TRUE); - - if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) { - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } else { - g_object_unref (ev->data.dom.from); - g_object_unref (ev->data.dom.to); - g_free (ev); - } - } + EHTMLEditor *editor; + EHTMLEditorCellDialog *dialog; + EContentEditor *cnt_editor; + + dialog = E_HTML_EDITOR_CELL_DIALOG (widget); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_object_unref (priv->cell); - priv->cell = NULL; + e_content_editor_on_cell_dialog_close (cnt_editor); GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->hide (widget); } @@ -842,7 +554,8 @@ e_html_editor_cell_dialog_init (EHTMLEditorCellDialog *dialog) gtk_grid_attach (grid, widget, 0, 0, 1, 1); dialog->priv->width_check = widget; - widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1); + widget = gtk_spin_button_new_with_range (1, 100, 1); + gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0); gtk_grid_attach (grid, widget, 1, 0, 1, 1); dialog->priv->width_edit = widget; @@ -855,14 +568,14 @@ e_html_editor_cell_dialog_init (EHTMLEditorCellDialog *dialog) G_BINDING_SYNC_CREATE); widget = gtk_combo_box_text_new (); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "unit-px", "px"); - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "unit-percent", "%"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px"); + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%"); gtk_grid_attach (grid, widget, 2, 0, 1, 1); dialog->priv->width_units = widget; - g_signal_connect_swapped ( + g_signal_connect ( widget, "changed", - G_CALLBACK (html_editor_cell_dialog_set_width), dialog); + G_CALLBACK (html_editor_cell_dialog_width_units_changed), dialog); e_binding_bind_property ( dialog->priv->width_check, "active", widget, "sensitive", @@ -975,23 +688,3 @@ e_html_editor_cell_dialog_new (EHTMLEditor *editor) "title", _("Cell Properties"), NULL)); } - -void -e_html_editor_cell_dialog_show (EHTMLEditorCellDialog *dialog, - WebKitDOMNode *cell) -{ - EHTMLEditorCellDialogClass *class; - - g_return_if_fail (E_IS_HTML_EDITOR_CELL_DIALOG (dialog)); - g_return_if_fail (cell != NULL); - - dialog->priv->cell = e_html_editor_dom_node_find_parent_element (cell, "TD"); - if (dialog->priv->cell == NULL) { - dialog->priv->cell = - e_html_editor_dom_node_find_parent_element (cell, "TH"); - } - - class = E_HTML_EDITOR_CELL_DIALOG_GET_CLASS (dialog); - GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog)); -} - diff --git a/e-util/e-html-editor-cell-dialog.h b/e-util/e-html-editor-cell-dialog.h index 3a7f608..68ae1bb 100644 --- a/e-util/e-html-editor-cell-dialog.h +++ b/e-util/e-html-editor-cell-dialog.h @@ -64,8 +64,6 @@ struct _EHTMLEditorCellDialogClass { GType e_html_editor_cell_dialog_get_type (void) G_GNUC_CONST; GtkWidget * e_html_editor_cell_dialog_new (EHTMLEditor *editor); -void e_html_editor_cell_dialog_show (EHTMLEditorCellDialog *dialog, - WebKitDOMNode *cell); G_END_DECLS diff --git a/e-util/e-html-editor-dialog.h b/e-util/e-html-editor-dialog.h index 37fc7a5..f1f5700 100644 --- a/e-util/e-html-editor-dialog.h +++ b/e-util/e-html-editor-dialog.h @@ -62,6 +62,16 @@ struct _EHTMLEditorDialogClass { GtkWindowClass parent_class; }; +#if 0 /* FIXME WK2 */ +struct _EContentEditorDialogInterface { + GTypeInterface parent_interface; + + void (*dialog_opened) (EContentEditorDialog *dialog); + + void (*dialog_closed) (EContentEditorDialog *dialog); +}; +#endif + GType e_html_editor_dialog_get_type (void) G_GNUC_CONST; EHTMLEditor * e_html_editor_dialog_get_editor (EHTMLEditorDialog *dialog); GtkBox * e_html_editor_dialog_get_button_box diff --git a/e-util/e-html-editor-find-dialog.c b/e-util/e-html-editor-find-dialog.c index 6fe61cf..f1ad2f2 100644 --- a/e-util/e-html-editor-find-dialog.c +++ b/e-util/e-html-editor-find-dialog.c @@ -41,6 +41,9 @@ struct _EHTMLEditorFindDialogPrivate { GtkWidget *find_button; GtkWidget *result_label; + + EContentEditor *cnt_editor; + gulong find_done_handler_id; }; G_DEFINE_TYPE ( @@ -56,6 +59,17 @@ reset_dialog (EHTMLEditorFindDialog *dialog) } static void +html_editor_find_dialog_hide (GtkWidget *widget) +{ + EHTMLEditorFindDialog *dialog = E_HTML_EDITOR_FIND_DIALOG (widget); + + e_content_editor_on_find_dialog_close (dialog->priv->cnt_editor); + + /* Chain up to parent's implementation */ + GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->hide (widget); +} + +static void html_editor_find_dialog_show (GtkWidget *widget) { EHTMLEditorFindDialog *dialog = E_HTML_EDITOR_FIND_DIALOG (widget); @@ -63,48 +77,45 @@ html_editor_find_dialog_show (GtkWidget *widget) reset_dialog (dialog); gtk_widget_grab_focus (dialog->priv->entry); + e_content_editor_on_find_dialog_open (dialog->priv->cnt_editor); + /* Chain up to parent's implementation */ GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->show (widget); } static void -html_editor_find_dialog_find_cb (EHTMLEditorFindDialog *dialog) +content_editor_find_done_cb (EContentEditor *cnt_editor, + guint match_count, + EHTMLEditorFindDialog *dialog) { - gboolean found; - EHTMLEditor *editor; - EHTMLEditorView *view; - - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - found = webkit_web_view_search_text ( - WEBKIT_WEB_VIEW (view), - gtk_entry_get_text ( - GTK_ENTRY (dialog->priv->entry)), - gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON ( - dialog->priv->case_sensitive)), - !gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON ( - dialog->priv->backwards)), - gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON ( - dialog->priv->wrap_search))); - - gtk_widget_set_sensitive (dialog->priv->find_button, found); - - /* We give focus to WebKit so that the selection is highlited. - * Without focus selection is not visible (at least with my default - * color scheme). The focus in fact is not given to WebKit, because - * this dialog is modal, but it satisfies it in a way that it paints - * the selection :) */ - gtk_widget_grab_focus (GTK_WIDGET (view)); - - if (!found) { - gtk_label_set_label ( - GTK_LABEL (dialog->priv->result_label), - N_("No match found")); + if (match_count) { + gtk_widget_hide (dialog->priv->result_label); + } else { + gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), _("No match found")); gtk_widget_show (dialog->priv->result_label); } + + gtk_widget_set_sensitive (dialog->priv->find_button, match_count > 0); +} + +static void +html_editor_find_dialog_find_cb (EHTMLEditorFindDialog *dialog) +{ + guint32 flags = E_CONTENT_EDITOR_FIND_NEXT; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->backwards))) + flags |= E_CONTENT_EDITOR_FIND_MODE_BACKWARDS; + + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive))) + flags |= E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->wrap_search))) + flags |= E_CONTENT_EDITOR_FIND_WRAP_AROUND; + + e_content_editor_find ( + dialog->priv->cnt_editor, + flags, + gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry))); } static gboolean @@ -125,13 +136,59 @@ entry_key_release_event (GtkWidget *widget, } static void +html_editor_find_dialog_dispose (GObject *object) +{ + EHTMLEditorFindDialogPrivate *priv; + + priv = E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE (object); + + if (priv->find_done_handler_id > 0) { + g_signal_handler_disconnect ( + priv->cnt_editor, + priv->find_done_handler_id); + priv->find_done_handler_id = 0; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_html_editor_find_dialog_parent_class)->dispose (object); +} + +static void +html_editor_find_dialog_constructed (GObject *object) +{ + EHTMLEditor *editor; + EHTMLEditorFindDialog *dialog; + EContentEditor *cnt_editor; + + dialog = E_HTML_EDITOR_FIND_DIALOG (object); + dialog->priv = E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE (dialog); + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + dialog->priv->find_done_handler_id = g_signal_connect ( + cnt_editor, "find-done", + G_CALLBACK (content_editor_find_done_cb), dialog); + + dialog->priv->cnt_editor = cnt_editor; + + G_OBJECT_CLASS (e_html_editor_find_dialog_parent_class)->constructed (object); +} + +static void e_html_editor_find_dialog_class_init (EHTMLEditorFindDialogClass *class) { + GObjectClass *object_class; GtkWidgetClass *widget_class; g_type_class_add_private (class, sizeof (EHTMLEditorFindDialogPrivate)); + object_class = G_OBJECT_CLASS (class); + object_class->constructed = html_editor_find_dialog_constructed; + object_class->dispose = html_editor_find_dialog_dispose; + widget_class = GTK_WIDGET_CLASS (class); + widget_class->hide = html_editor_find_dialog_hide; widget_class->show = html_editor_find_dialog_show; } @@ -217,9 +274,8 @@ e_html_editor_find_dialog_new (EHTMLEditor *editor) void e_html_editor_find_dialog_find_next (EHTMLEditorFindDialog *dialog) { - if (gtk_entry_get_text_length (GTK_ENTRY (dialog->priv->entry)) == 0) { + if (gtk_entry_get_text_length (GTK_ENTRY (dialog->priv->entry)) == 0) return; - } html_editor_find_dialog_find_cb (dialog); } diff --git a/e-util/e-html-editor-hrule-dialog.c b/e-util/e-html-editor-hrule-dialog.c index bb92276..5dc6860 100644 --- a/e-util/e-html-editor-hrule-dialog.c +++ b/e-util/e-html-editor-hrule-dialog.c @@ -23,11 +23,8 @@ #endif #include "e-html-editor-hrule-dialog.h" -#include "e-html-editor-utils.h" -#include "e-html-editor-view.h" #include <glib/gi18n-lib.h> -#include <webkit/webkitdom.h> #include <stdlib.h> #define E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE(obj) \ @@ -41,10 +38,6 @@ struct _EHTMLEditorHRuleDialogPrivate { GtkWidget *alignment_combo; GtkWidget *shaded_check; - - WebKitDOMHTMLHRElement *hr_element; - - EHTMLEditorViewHistoryEvent *history_event; }; G_DEFINE_TYPE ( @@ -55,185 +48,148 @@ G_DEFINE_TYPE ( static void html_editor_hrule_dialog_set_alignment (EHTMLEditorHRuleDialog *dialog) { - const gchar *alignment; + EHTMLEditor *editor; + EContentEditor *cnt_editor; + const gchar *value; - g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element)); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - alignment = gtk_combo_box_get_active_id ( - GTK_COMBO_BOX (dialog->priv->alignment_combo)); + value = gtk_combo_box_get_active_id ( + GTK_COMBO_BOX (dialog->priv->alignment_combo)); - webkit_dom_htmlhr_element_set_align (dialog->priv->hr_element, alignment); + e_content_editor_h_rule_set_align (cnt_editor, value); } static void html_editor_hrule_dialog_get_alignment (EHTMLEditorHRuleDialog *dialog) { - gchar *alignment; - - g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element)); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gchar *value; - alignment = webkit_dom_htmlhr_element_get_align (dialog->priv->hr_element); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - gtk_combo_box_set_active_id ( - GTK_COMBO_BOX (dialog->priv->alignment_combo), alignment); - g_free (alignment); + value = e_content_editor_h_rule_get_align (cnt_editor); + if (value && *value) + gtk_combo_box_set_active_id ( + GTK_COMBO_BOX (dialog->priv->alignment_combo), value); + g_free (value); } static void html_editor_hrule_dialog_set_size (EHTMLEditorHRuleDialog *dialog) { - gchar *size; - - g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element)); - - size = g_strdup_printf ( - "%d", - (gint) gtk_spin_button_get_value ( - GTK_SPIN_BUTTON (dialog->priv->size_edit))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gint value; - webkit_dom_htmlhr_element_set_size (dialog->priv->hr_element, size); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_free (size); + value = gtk_spin_button_get_value_as_int GTK_SPIN_BUTTON (dialog->priv->size_edit); + e_content_editor_h_rule_set_size (cnt_editor, value); } static void html_editor_hrule_dialog_get_size (EHTMLEditorHRuleDialog *dialog) { - gchar *size; - gint size_int = 0; + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gint value; - g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element)); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - size = webkit_dom_htmlhr_element_get_size (dialog->priv->hr_element); - if (size && *size) { - size_int = atoi (size); - } - - if (size_int == 0) { - size_int = 2; - } + value = e_content_editor_h_rule_get_size (cnt_editor); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->size_edit), (gdouble) size_int); - - g_free (size); + GTK_SPIN_BUTTON (dialog->priv->size_edit), (gdouble) value); } static void html_editor_hrule_dialog_set_width (EHTMLEditorHRuleDialog *dialog) { - gchar *width, *units; + EHTMLEditor *editor; + EContentEditor *cnt_editor; - g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element)); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - units = gtk_combo_box_text_get_active_text ( - GTK_COMBO_BOX_TEXT (dialog->priv->unit_combo)); - width = g_strdup_printf ( - "%d%s", - (gint) gtk_spin_button_get_value ( + e_content_editor_h_rule_set_width ( + cnt_editor, + gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON (dialog->priv->width_edit)), - units); - - webkit_dom_htmlhr_element_set_width (dialog->priv->hr_element, width); - - g_free (units); - g_free (width); + (gtk_combo_box_get_active ( + GTK_COMBO_BOX (dialog->priv->unit_combo)) == 0) ? + E_CONTENT_EDITOR_UNIT_PIXEL : + E_CONTENT_EDITOR_UNIT_PERCENTAGE); } static void html_editor_hrule_dialog_get_width (EHTMLEditorHRuleDialog *dialog) { - gchar *width; - const gchar *units; - gint width_int = 0; - - g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element)); - - width = webkit_dom_htmlhr_element_get_width (dialog->priv->hr_element); - if (width && *width) { - width_int = atoi (width); - - if (strstr (width, "%") != NULL) { - units = "units-percent"; - } else { - units = "units-px"; - } - } + EHTMLEditor *editor; + EContentEditor *cnt_editor; + EContentEditorUnit unit; + gint value; - if (width_int == 0) { - width_int = 100; - units = "units-percent"; - } + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + value = e_content_editor_h_rule_get_width (cnt_editor, &unit); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->width_edit), (gdouble) width_int); + GTK_SPIN_BUTTON (dialog->priv->width_edit), + value == 0 && unit == E_CONTENT_EDITOR_UNIT_PERCENTAGE ? 100 : value); gtk_combo_box_set_active_id ( - GTK_COMBO_BOX (dialog->priv->unit_combo), units); - - g_free (width); + GTK_COMBO_BOX (dialog->priv->unit_combo), + unit == E_CONTENT_EDITOR_UNIT_PIXEL ? "units-px" : "units-percent"); } static void html_editor_hrule_dialog_set_shading (EHTMLEditorHRuleDialog *dialog) { - gboolean no_shade; + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gboolean value; - g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element)); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - no_shade = !gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (dialog->priv->shaded_check)); + value = !gtk_toggle_button_get_active ( + GTK_TOGGLE_BUTTON (dialog->priv->shaded_check)); - webkit_dom_htmlhr_element_set_no_shade (dialog->priv->hr_element, no_shade); + e_content_editor_h_rule_set_no_shade (cnt_editor, value); } static void html_editor_hrule_dialog_get_shading (EHTMLEditorHRuleDialog *dialog) { - g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element)); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gboolean value = FALSE; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + value = e_content_editor_h_rule_get_no_shade (cnt_editor); gtk_toggle_button_set_active ( - GTK_TOGGLE_BUTTON (dialog->priv->shaded_check), - !webkit_dom_htmlhr_element_get_no_shade (dialog->priv->hr_element)); + GTK_TOGGLE_BUTTON (dialog->priv->shaded_check), !value); } static void html_editor_hrule_dialog_hide (GtkWidget *widget) { - EHTMLEditorHRuleDialogPrivate *priv; - EHTMLEditorViewHistoryEvent *ev; - - priv = E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE (widget); - ev = priv->history_event; - - if (ev) { - EHTMLEditorHRuleDialog *dialog; - EHTMLEditor *editor; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - - dialog = E_HTML_EDITOR_HRULE_DIALOG (widget); - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - ev->data.dom.to = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (priv->hr_element), FALSE); - - if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) { - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - - if (!ev->data.dom.from) - g_object_unref (priv->hr_element); - } else { - g_object_unref (ev->data.dom.from); - g_object_unref (ev->data.dom.to); - g_free (ev); - } - } + EHTMLEditor *editor; + EHTMLEditorHRuleDialog *dialog; + EContentEditor *cnt_editor; + + dialog = E_HTML_EDITOR_HRULE_DIALOG (widget); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - priv->hr_element = NULL; + e_content_editor_on_h_rule_dialog_close (cnt_editor); GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->hide (widget); } @@ -243,55 +199,21 @@ html_editor_hrule_dialog_show (GtkWidget *widget) { EHTMLEditorHRuleDialog *dialog; EHTMLEditor *editor; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - - WebKitDOMDocument *document; + EContentEditor *cnt_editor; + gboolean created_new_h_rule = FALSE; dialog = E_HTML_EDITOR_HRULE_DIALOG (widget); editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - EHTMLEditorViewHistoryEvent *ev; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_HRULE_DIALOG; - - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - if (dialog->priv->hr_element) - ev->data.dom.from = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (dialog->priv->hr_element), FALSE); - else - ev->data.dom.from = NULL; - dialog->priv->history_event = ev; - } - - if (!dialog->priv->hr_element) { - WebKitDOMElement *selection_start, *parent, *rule; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - e_html_editor_selection_save (selection); - - selection_start = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start)); - - rule = webkit_dom_document_create_element (document, "HR", NULL); - - /* Insert horizontal rule into body below the caret */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)), - WEBKIT_DOM_NODE (rule), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)), - NULL); - - e_html_editor_selection_restore (selection); + cnt_editor = e_html_editor_get_content_editor (editor); - dialog->priv->hr_element = WEBKIT_DOM_HTMLHR_ELEMENT (rule); + created_new_h_rule = e_content_editor_on_h_rule_dialog_open (cnt_editor); + if (!created_new_h_rule) { + html_editor_hrule_dialog_get_alignment (dialog); + html_editor_hrule_dialog_get_size (dialog); + html_editor_hrule_dialog_get_width (dialog); + html_editor_hrule_dialog_get_shading (dialog); + } else { /* For new rule reset the values to default */ gtk_spin_button_set_value ( GTK_SPIN_BUTTON (dialog->priv->width_edit), 100.0); @@ -309,12 +231,7 @@ html_editor_hrule_dialog_show (GtkWidget *widget) html_editor_hrule_dialog_set_alignment (dialog); html_editor_hrule_dialog_set_shading (dialog); - e_html_editor_view_set_changed (view, TRUE); - } else { - html_editor_hrule_dialog_get_alignment (dialog); - html_editor_hrule_dialog_get_size (dialog); - html_editor_hrule_dialog_get_width (dialog); - html_editor_hrule_dialog_get_shading (dialog); + e_content_editor_set_changed (cnt_editor, TRUE); } /* Chain up to parent implementation */ @@ -448,17 +365,3 @@ e_html_editor_hrule_dialog_new (EHTMLEditor *editor) "title", _("Rule properties"), NULL)); } - -void -e_html_editor_hrule_dialog_show (EHTMLEditorHRuleDialog *dialog, - WebKitDOMNode *rule) -{ - EHTMLEditorHRuleDialogClass *class; - - g_return_if_fail (E_IS_HTML_EDITOR_HRULE_DIALOG (dialog)); - - dialog->priv->hr_element = rule ? WEBKIT_DOM_HTMLHR_ELEMENT (rule) : NULL; - - class = E_HTML_EDITOR_HRULE_DIALOG_GET_CLASS (dialog); - GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog)); -} diff --git a/e-util/e-html-editor-hrule-dialog.h b/e-util/e-html-editor-hrule-dialog.h index b616da9..876fc25 100644 --- a/e-util/e-html-editor-hrule-dialog.h +++ b/e-util/e-html-editor-hrule-dialog.h @@ -64,8 +64,6 @@ struct _EHTMLEditorHRuleDialogClass { GType e_html_editor_hrule_dialog_get_type (void) G_GNUC_CONST; GtkWidget * e_html_editor_hrule_dialog_new (EHTMLEditor *editor); -void e_html_editor_hrule_dialog_show (EHTMLEditorHRuleDialog *dialog, - WebKitDOMNode *rule); G_END_DECLS diff --git a/e-util/e-html-editor-image-dialog.c b/e-util/e-html-editor-image-dialog.c index 5eb7909..50db162 100644 --- a/e-util/e-html-editor-image-dialog.c +++ b/e-util/e-html-editor-image-dialog.c @@ -27,7 +27,6 @@ #include <stdlib.h> #include <glib/gi18n-lib.h> -#include "e-html-editor-utils.h" #include "e-image-chooser-dialog.h" #define E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE(obj) \ @@ -50,10 +49,6 @@ struct _EHTMLEditorImageDialogPrivate { GtkWidget *url_edit; GtkWidget *test_url_button; - - WebKitDOMHTMLImageElement *image; - - EHTMLEditorViewHistoryEvent *history_event; }; G_DEFINE_TYPE ( @@ -65,19 +60,16 @@ static void html_editor_image_dialog_set_src (EHTMLEditorImageDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorSelection *editor_selection; - EHTMLEditorView *view; + EContentEditor *cnt_editor; gchar *uri; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - editor_selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); uri = gtk_file_chooser_get_uri ( GTK_FILE_CHOOSER (dialog->priv->file_chooser)); - e_html_editor_selection_replace_image_src ( - editor_selection, WEBKIT_DOM_ELEMENT (dialog->priv->image), uri); + e_content_editor_image_set_src (cnt_editor, uri); g_free (uri); } @@ -85,22 +77,34 @@ html_editor_image_dialog_set_src (EHTMLEditorImageDialog *dialog) static void html_editor_image_dialog_set_alt (EHTMLEditorImageDialog *dialog) { - webkit_dom_html_image_element_set_alt ( - dialog->priv->image, - gtk_entry_get_text (GTK_ENTRY (dialog->priv->description_edit))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + const gchar *value; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + value = gtk_entry_get_text (GTK_ENTRY (dialog->priv->description_edit)); + + e_content_editor_image_set_alt (cnt_editor, value); } static void html_editor_image_dialog_set_width (EHTMLEditorImageDialog *dialog) { + EHTMLEditor *editor; + EContentEditor *cnt_editor; gint requested; - gulong natural; + gint32 natural = 0; gint width; - natural = webkit_dom_html_image_element_get_natural_width ( - dialog->priv->image); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + natural = e_content_editor_image_get_natural_width (cnt_editor); + requested = gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->width_edit)); + GTK_SPIN_BUTTON (dialog->priv->width_edit)); switch (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->width_units))) { case 0: /* px */ @@ -119,18 +123,23 @@ html_editor_image_dialog_set_width (EHTMLEditorImageDialog *dialog) return; } - webkit_dom_html_image_element_set_width (dialog->priv->image, width); + e_content_editor_image_set_width (cnt_editor, width); } static void html_editor_image_dialog_set_width_units (EHTMLEditorImageDialog *dialog) { + EHTMLEditor *editor; + EContentEditor *cnt_editor; gint requested; - gulong natural; + gint32 natural = 0; gint width = 0; - natural = webkit_dom_html_image_element_get_natural_width ( - dialog->priv->image); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + natural = e_content_editor_image_get_natural_width (cnt_editor); + requested = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON (dialog->priv->width_edit)); @@ -143,8 +152,6 @@ html_editor_image_dialog_set_width_units (EHTMLEditorImageDialog *dialog) } else { width = natural; } - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->image), "style"); gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE); break; @@ -154,38 +161,38 @@ html_editor_image_dialog_set_width_units (EHTMLEditorImageDialog *dialog) } else { width = 100; } - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->image), "style"); gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE); break; case 2: /* follow */ - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->image), - "style", - "width: auto;", - NULL); gtk_widget_set_sensitive (dialog->priv->width_edit, FALSE); break; } - if (width != 0) { + e_content_editor_image_set_width_follow ( + cnt_editor, !gtk_widget_get_sensitive (dialog->priv->width_edit)); + + if (width != 0) gtk_spin_button_set_value ( GTK_SPIN_BUTTON (dialog->priv->width_edit), width); - } } static void html_editor_image_dialog_set_height (EHTMLEditorImageDialog *dialog) { + EHTMLEditor *editor; + EContentEditor *cnt_editor; gint requested; - gulong natural; + gint32 natural = 0; gint height; - natural = webkit_dom_html_image_element_get_natural_height ( - dialog->priv->image); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + natural = e_content_editor_image_get_natural_height (cnt_editor); + requested = gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->height_edit)); + GTK_SPIN_BUTTON (dialog->priv->height_edit)); switch (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->height_units))) { case 0: /* px */ @@ -204,20 +211,25 @@ html_editor_image_dialog_set_height (EHTMLEditorImageDialog *dialog) return; } - webkit_dom_html_image_element_set_height (dialog->priv->image, height); + e_content_editor_image_set_height (cnt_editor, height); } static void html_editor_image_dialog_set_height_units (EHTMLEditorImageDialog *dialog) { + EHTMLEditor *editor; + EContentEditor *cnt_editor; gint requested; - gulong natural; + gulong natural = 0; gint height = -1; - natural = webkit_dom_html_image_element_get_natural_height ( - dialog->priv->image); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + natural = e_content_editor_image_get_natural_height (cnt_editor); + requested = gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->height_edit)); + GTK_SPIN_BUTTON (dialog->priv->height_edit)); switch (gtk_combo_box_get_active ( GTK_COMBO_BOX (dialog->priv->height_units))) { @@ -228,8 +240,6 @@ html_editor_image_dialog_set_height_units (EHTMLEditorImageDialog *dialog) } else { height = natural; } - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->image), "style"); gtk_widget_set_sensitive (dialog->priv->height_edit, TRUE); break; @@ -239,116 +249,95 @@ html_editor_image_dialog_set_height_units (EHTMLEditorImageDialog *dialog) } else { height = 100; } - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->image), "style"); gtk_widget_set_sensitive (dialog->priv->height_edit, TRUE); break; case 2: /* follow */ - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->image), - "style", - "height: auto;", - NULL); gtk_widget_set_sensitive (dialog->priv->height_edit, FALSE); break; } - if (height != -1) { + e_content_editor_image_set_height_follow ( + cnt_editor, !gtk_widget_get_sensitive (dialog->priv->height_edit)); + + if (height != -1) gtk_spin_button_set_value ( GTK_SPIN_BUTTON (dialog->priv->height_edit), height); - } } static void html_editor_image_dialog_set_alignment (EHTMLEditorImageDialog *dialog) { - webkit_dom_html_image_element_set_align ( - dialog->priv->image, - gtk_combo_box_get_active_id ( - GTK_COMBO_BOX (dialog->priv->alignment))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + const gchar *value; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + value = gtk_combo_box_get_active_id (GTK_COMBO_BOX (dialog->priv->alignment)); + e_content_editor_image_set_align (cnt_editor, value); } static void html_editor_image_dialog_set_x_padding (EHTMLEditorImageDialog *dialog) { - webkit_dom_html_image_element_set_hspace ( - dialog->priv->image, - gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->x_padding_edit))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gint value; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + value = gtk_spin_button_get_value_as_int ( + GTK_SPIN_BUTTON (dialog->priv->x_padding_edit)); + e_content_editor_image_set_hspace (cnt_editor, value); } static void html_editor_image_dialog_set_y_padding (EHTMLEditorImageDialog *dialog) { - webkit_dom_html_image_element_set_vspace ( - dialog->priv->image, - gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->y_padding_edit))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gint value; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + value = gtk_spin_button_get_value_as_int ( + GTK_SPIN_BUTTON (dialog->priv->y_padding_edit)); + e_content_editor_image_set_vspace (cnt_editor, value); } static void html_editor_image_dialog_set_border (EHTMLEditorImageDialog *dialog) { - gchar *val; + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gint value; - val = g_strdup_printf ( - "%d", gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->border_edit))); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - webkit_dom_html_image_element_set_border (dialog->priv->image, val); + value = gtk_spin_button_get_value_as_int ( + GTK_SPIN_BUTTON (dialog->priv->border_edit)); - g_free (val); + e_content_editor_image_set_border (cnt_editor, value); } static void html_editor_image_dialog_set_url (EHTMLEditorImageDialog *dialog) { - WebKitDOMElement *link; - const gchar *url; - - url = gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)); - link = e_html_editor_dom_node_find_parent_element ( - WEBKIT_DOM_NODE (dialog->priv->image), "A"); - - if (link) { - if (!url || !*url) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (link)), - WEBKIT_DOM_NODE (dialog->priv->image), - WEBKIT_DOM_NODE (link), NULL); - webkit_dom_node_remove_child ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (link)), - WEBKIT_DOM_NODE (link), NULL); - } else { - webkit_dom_html_anchor_element_set_href ( - WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url); - } - } else { - if (url && *url) { - WebKitDOMDocument *document; - - document = webkit_dom_node_get_owner_document ( - WEBKIT_DOM_NODE (dialog->priv->image)); - link = webkit_dom_document_create_element ( - document, "A", NULL); - - webkit_dom_html_anchor_element_set_href ( - WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (dialog->priv->image)), - WEBKIT_DOM_NODE (link), - WEBKIT_DOM_NODE (dialog->priv->image), NULL); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (link), - WEBKIT_DOM_NODE (dialog->priv->image), NULL); - } - } + EHTMLEditor *editor; + EContentEditor *cnt_editor; + const gchar *value; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + value = gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)); + + e_content_editor_image_set_url (cnt_editor, value); } static void @@ -364,42 +353,21 @@ html_editor_image_dialog_test_url (EHTMLEditorImageDialog *dialog) static void html_editor_image_dialog_show (GtkWidget *widget) { - EHTMLEditorImageDialog *dialog; EHTMLEditor *editor; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - WebKitDOMElement *link; - gchar *tmp; - glong val; + EHTMLEditorImageDialog *dialog; + EContentEditor *cnt_editor; + gchar *value; dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget); - - if (!dialog->priv->image) { - return; - } - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - EHTMLEditorViewHistoryEvent *ev; + e_content_editor_on_image_dialog_open (cnt_editor); - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_IMAGE_DIALOG; - - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - ev->data.dom.from = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (dialog->priv->image), FALSE); - dialog->priv->history_event = ev; - } - - tmp = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->image), "data-uri"); - if (tmp && *tmp) { + value = e_content_editor_image_get_src (cnt_editor); + if (value && *value) { gtk_file_chooser_set_uri ( - GTK_FILE_CHOOSER (dialog->priv->file_chooser), tmp); + GTK_FILE_CHOOSER (dialog->priv->file_chooser), value); gtk_widget_set_sensitive ( GTK_WIDGET (dialog->priv->file_chooser), TRUE); } else { @@ -408,56 +376,49 @@ html_editor_image_dialog_show (GtkWidget *widget) gtk_widget_set_sensitive ( GTK_WIDGET (dialog->priv->file_chooser), FALSE); } - g_free (tmp); + g_free (value); - tmp = webkit_dom_html_image_element_get_alt (dialog->priv->image); - gtk_entry_set_text (GTK_ENTRY (dialog->priv->description_edit), tmp ? tmp : ""); - g_free (tmp); + value = e_content_editor_image_get_alt (cnt_editor); + gtk_entry_set_text ( + GTK_ENTRY (dialog->priv->description_edit), value ? value : ""); + g_free (value); - val = webkit_dom_html_image_element_get_width (dialog->priv->image); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->width_edit), val); + GTK_SPIN_BUTTON (dialog->priv->width_edit), + e_content_editor_image_get_width (cnt_editor)); + gtk_combo_box_set_active_id ( GTK_COMBO_BOX (dialog->priv->width_units), "units-px"); - val = webkit_dom_html_image_element_get_height (dialog->priv->image); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->height_edit), val); + GTK_SPIN_BUTTON (dialog->priv->height_edit), + e_content_editor_image_get_height (cnt_editor)); + gtk_combo_box_set_active_id ( GTK_COMBO_BOX (dialog->priv->height_units), "units-px"); - tmp = webkit_dom_html_image_element_get_border (dialog->priv->image); - if (tmp && *tmp) { - gint border; - - border = atoi (tmp); - gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->border_edit), border); - } - g_free (tmp); + gtk_spin_button_set_value ( + GTK_SPIN_BUTTON (dialog->priv->border_edit), + e_content_editor_image_get_border (cnt_editor)); - tmp = webkit_dom_html_image_element_get_align (dialog->priv->image); + value = e_content_editor_image_get_align (cnt_editor); gtk_combo_box_set_active_id ( GTK_COMBO_BOX (dialog->priv->alignment), - (tmp && *tmp) ? tmp : "bottom"); - g_free (tmp); + (value && *value) ? value : "bottom"); + g_free (value); - val = webkit_dom_html_image_element_get_hspace (dialog->priv->image); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->x_padding_edit), val); + GTK_SPIN_BUTTON (dialog->priv->y_padding_edit), + e_content_editor_image_get_hspace (cnt_editor)); - val = webkit_dom_html_image_element_get_vspace (dialog->priv->image); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->y_padding_edit), val); - - link = e_html_editor_dom_node_find_parent_element ( - WEBKIT_DOM_NODE (dialog->priv->image), "A"); - if (link) { - tmp = webkit_dom_html_anchor_element_get_href ( - WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link)); - gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), tmp); - g_free (tmp); - } + GTK_SPIN_BUTTON (dialog->priv->y_padding_edit), + e_content_editor_image_get_vspace (cnt_editor)); + + value = e_content_editor_image_get_url (cnt_editor); + if (value && *value) + gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), value); + g_free (value); /* Chain up to parent implementation */ GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->show (widget); @@ -466,33 +427,15 @@ html_editor_image_dialog_show (GtkWidget *widget) static void html_editor_image_dialog_hide (GtkWidget *widget) { - EHTMLEditorImageDialogPrivate *priv; - EHTMLEditorViewHistoryEvent *ev; - - priv = E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE (widget); - ev = priv->history_event; - - if (ev) { - EHTMLEditorImageDialog *dialog; - EHTMLEditor *editor; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - - dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget); - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - ev->data.dom.to = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (priv->image), FALSE); + EHTMLEditor *editor; + EHTMLEditorImageDialog *dialog; + EContentEditor *cnt_editor; - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } + dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_object_unref (priv->image); - priv->image = NULL; + e_content_editor_on_image_dialog_close (cnt_editor); GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->hide (widget); } @@ -742,19 +685,12 @@ e_html_editor_image_dialog_new (EHTMLEditor *editor) } void -e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog, - WebKitDOMNode *image) +e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog) { EHTMLEditorImageDialogClass *class; g_return_if_fail (E_IS_HTML_EDITOR_IMAGE_DIALOG (dialog)); - if (image) { - dialog->priv->image = WEBKIT_DOM_HTML_IMAGE_ELEMENT (image); - } else { - dialog->priv->image = NULL; - } - class = E_HTML_EDITOR_IMAGE_DIALOG_GET_CLASS (dialog); GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog)); } diff --git a/e-util/e-html-editor-image-dialog.h b/e-util/e-html-editor-image-dialog.h index efdbaf9..a4d0304 100644 --- a/e-util/e-html-editor-image-dialog.h +++ b/e-util/e-html-editor-image-dialog.h @@ -64,8 +64,7 @@ struct _EHTMLEditorImageDialogClass { GType e_html_editor_image_dialog_get_type (void) G_GNUC_CONST; GtkWidget * e_html_editor_image_dialog_new (EHTMLEditor *editor); -void e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog, - WebKitDOMNode *image); +void e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog); G_END_DECLS diff --git a/e-util/e-html-editor-link-dialog.c b/e-util/e-html-editor-link-dialog.c index 78bc97d..5db93ed 100644 --- a/e-util/e-html-editor-link-dialog.c +++ b/e-util/e-html-editor-link-dialog.c @@ -23,10 +23,6 @@ #endif #include "e-html-editor-link-dialog.h" -#include "e-html-editor-selection.h" -#include "e-html-editor-utils.h" -#include "e-html-editor-view.h" -#include "e-web-view.h" #include <glib/gi18n-lib.h> @@ -48,11 +44,6 @@ struct _EHTMLEditorLinkDialogPrivate { GtkWidget *ok_button; gboolean label_autofill; - gboolean unlinking; - - WebKitDOMElement *link_element; - - EHTMLEditorViewHistoryEvent *history_event; }; static void @@ -94,14 +85,12 @@ static void html_editor_link_dialog_remove_link (EHTMLEditorLinkDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_unlink (selection); - dialog->priv->unlinking = TRUE; + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_selection_unlink (cnt_editor); gtk_widget_hide (GTK_WIDGET (dialog)); } @@ -110,108 +99,15 @@ static void html_editor_link_dialog_ok (EHTMLEditorLinkDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - WebKitDOMDocument *document; + EContentEditor *cnt_editor; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (dialog->priv->link_element) { - WebKitDOMElement *element; - - webkit_dom_html_anchor_element_set_href ( - WEBKIT_DOM_HTML_ANCHOR_ELEMENT (dialog->priv->link_element), - gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit))); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (dialog->priv->link_element), - gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit)), - NULL); - - element = webkit_dom_document_create_element (document, "SPAN", NULL); - webkit_dom_element_set_id (element, "-x-evo-selection-end-marker"); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (dialog->priv->link_element)), - WEBKIT_DOM_NODE (element), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (dialog->priv->link_element)), - NULL); - - element = webkit_dom_document_create_element (document, "SPAN", NULL); - webkit_dom_element_set_id (element, "-x-evo-selection-start-marker"); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (dialog->priv->link_element)), - WEBKIT_DOM_NODE (element), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (dialog->priv->link_element)), - NULL); - - e_html_editor_selection_restore (selection); - } else { - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - e_html_editor_selection_restore (selection); - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - if (webkit_dom_range_get_collapsed (range, NULL)) { - WebKitDOMElement *selection_marker; - WebKitDOMElement *anchor; - - e_html_editor_selection_save (selection); - selection_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - anchor = webkit_dom_document_create_element (document, "A", NULL); - webkit_dom_element_set_attribute ( - anchor, "href", gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)), NULL); - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (anchor), - gtk_entry_get_text ( - GTK_ENTRY (dialog->priv->label_edit)), - NULL); - dialog->priv->link_element = anchor; - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_marker)), - WEBKIT_DOM_NODE (anchor), - WEBKIT_DOM_NODE (selection_marker), - NULL); - e_html_editor_selection_restore (selection); - } else { - gchar *text; - - text = webkit_dom_range_get_text (range); - if (text && *text) { - WebKitDOMElement *selection_marker; - WebKitDOMNode *parent; - - e_html_editor_selection_create_link ( - selection, gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit))); - - dialog->priv->history_event->data.dom.from = - WEBKIT_DOM_NODE (webkit_dom_document_create_text_node (document, text)); - - e_html_editor_selection_save (selection); - selection_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - parent = webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_marker)); - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) - dialog->priv->link_element = WEBKIT_DOM_ELEMENT (parent); - e_html_editor_selection_restore (selection); - webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); - } - g_free (text); - } - - g_object_unref (range); - g_object_unref (dom_selection); - } + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_link_set_values ( + cnt_editor, + gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)), + gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit))); gtk_widget_hide (GTK_WIDGET (dialog)); } @@ -220,7 +116,7 @@ static gboolean html_editor_link_dialog_entry_key_pressed (EHTMLEditorLinkDialog *dialog, GdkEventKey *event) { - /* We can't do thins in key_released, because then you could not open + /* We can't do things in key_released, because then you could not open * this dialog from main menu by pressing enter on Insert->Link action */ if (event->keyval == GDK_KEY_Return) { html_editor_link_dialog_ok (dialog); @@ -231,139 +127,66 @@ html_editor_link_dialog_entry_key_pressed (EHTMLEditorLinkDialog *dialog, } static void +html_editor_link_dialog_hide (GtkWidget *widget) +{ + EHTMLEditor *editor; + EHTMLEditorLinkDialog *dialog; + EContentEditor *cnt_editor; + + dialog = E_HTML_EDITOR_LINK_DIALOG (widget); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_on_link_dialog_close (cnt_editor); + + /* Chain up to parent implementation */ + GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->hide (widget); +} + +static void html_editor_link_dialog_show (GtkWidget *widget) { EHTMLEditor *editor; EHTMLEditorLinkDialog *dialog; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; + EContentEditor *cnt_editor; + gchar *href = NULL, *text = NULL; dialog = E_HTML_EDITOR_LINK_DIALOG (widget); editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); /* Reset to default values */ gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), "http://"); gtk_entry_set_text (GTK_ENTRY (dialog->priv->label_edit), ""); gtk_widget_set_sensitive (dialog->priv->label_edit, TRUE); gtk_widget_set_sensitive (dialog->priv->remove_link_button, TRUE); - dialog->priv->label_autofill = TRUE; - dialog->priv->unlinking = FALSE; - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - EHTMLEditorViewHistoryEvent *ev; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_LINK_DIALOG; - - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - if (dialog->priv->link_element) - ev->data.dom.from = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (dialog->priv->link_element), TRUE); - else - ev->data.dom.from = NULL; - dialog->priv->history_event = ev; - } - if (dialog->priv->link_element) { - gchar *href, *text; + dialog->priv->label_autofill = TRUE; - href = webkit_dom_element_get_attribute (dialog->priv->link_element, "href"); - text = webkit_dom_html_element_get_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (dialog->priv->link_element)); + e_content_editor_on_link_dialog_open (cnt_editor); + e_content_editor_link_get_values (cnt_editor, &href, &text); + if (href && *href) gtk_entry_set_text ( GTK_ENTRY (dialog->priv->url_edit), href); + else + gtk_widget_set_sensitive ( + dialog->priv->remove_link_button, FALSE); + + g_free (href); + + if (text && *text) { gtk_entry_set_text ( GTK_ENTRY (dialog->priv->label_edit), text); - - g_free (text); - g_free (href); - } else { - gchar *text; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - /* No selection at all */ - if (!dom_selection || webkit_dom_dom_selection_get_range_count (dom_selection) < 1) - gtk_widget_set_sensitive (dialog->priv->remove_link_button, FALSE); - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - text = webkit_dom_range_get_text (range); - if (text && *text) { - gtk_entry_set_text ( - GTK_ENTRY (dialog->priv->label_edit), text); - gtk_widget_set_sensitive ( - dialog->priv->label_edit, FALSE); - gtk_widget_set_sensitive ( - dialog->priv->remove_link_button, FALSE); - } - g_free (text); - - g_object_unref (range); - g_object_unref (dom_selection); - - e_html_editor_selection_save (selection); + dialog->priv->label_autofill = FALSE; } + g_free (text); /* Chain up to parent implementation */ GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->show (widget); } static void -html_editor_link_dialog_hide (GtkWidget *widget) -{ - EHTMLEditorLinkDialogPrivate *priv; - EHTMLEditorViewHistoryEvent *ev; - - priv = E_HTML_EDITOR_LINK_DIALOG_GET_PRIVATE (widget); - ev = priv->history_event; - - if (priv->unlinking || !priv->link_element) { - g_clear_object (&ev->data.dom.from); - g_free (ev); - } else if (ev) { - EHTMLEditorLinkDialog *dialog; - EHTMLEditor *editor; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - - dialog = E_HTML_EDITOR_LINK_DIALOG (widget); - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - ev->data.dom.to = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (priv->link_element), TRUE); - - if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) { - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - if (!ev->data.dom.from) - g_object_unref (priv->link_element); - } else { - g_object_unref (ev->data.dom.from); - g_object_unref (ev->data.dom.to); - g_free (ev); - } - } - - priv->link_element = NULL; - - GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->hide (widget); -} - -static void e_html_editor_link_dialog_class_init (EHTMLEditorLinkDialogClass *class) { GtkWidgetClass *widget_class; @@ -383,7 +206,6 @@ e_html_editor_link_dialog_init (EHTMLEditorLinkDialog *dialog) GtkWidget *widget; dialog->priv = E_HTML_EDITOR_LINK_DIALOG_GET_PRIVATE (dialog); - dialog->priv->link_element = NULL; main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog)); @@ -454,17 +276,3 @@ e_html_editor_link_dialog_new (EHTMLEditor *editor) "title", _("Link Properties"), NULL)); } - -void -e_html_editor_link_dialog_show (EHTMLEditorLinkDialog *dialog, - WebKitDOMNode *link) -{ - EHTMLEditorLinkDialogClass *class; - - g_return_if_fail (E_IS_HTML_EDITOR_LINK_DIALOG (dialog)); - - dialog->priv->link_element = link ? WEBKIT_DOM_ELEMENT (link) : NULL; - - class = E_HTML_EDITOR_LINK_DIALOG_GET_CLASS (dialog); - GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog)); -} diff --git a/e-util/e-html-editor-link-dialog.h b/e-util/e-html-editor-link-dialog.h index c3a9709..a1e426f 100644 --- a/e-util/e-html-editor-link-dialog.h +++ b/e-util/e-html-editor-link-dialog.h @@ -65,8 +65,6 @@ GType e_html_editor_link_dialog_get_type (void) G_GNUC_CONST; GtkWidget * e_html_editor_link_dialog_new (EHTMLEditor *editor); -void e_html_editor_link_dialog_show (EHTMLEditorLinkDialog *dialog, - WebKitDOMNode *link); G_END_DECLS #endif /* E_HTML_EDITOR_LINK_DIALOG_H */ diff --git a/e-util/e-html-editor-manager.ui b/e-util/e-html-editor-manager.ui index 97a4861..fa7bc0b 100644 --- a/e-util/e-html-editor-manager.ui +++ b/e-util/e-html-editor-manager.ui @@ -27,7 +27,7 @@ <placeholder name='pre-insert-menu'> <menu action='view-menu'> <placeholder name='view-menu-top'/> - <menuitem action='webkit-inspector'/> + <placeholder name='view-menu-custom'/> <separator/> </menu> </placeholder> @@ -69,7 +69,6 @@ <menuitem action='style-normal'/> <menuitem action='style-preformat'/> <menuitem action='style-address'/> - <menuitem action='style-blockquote'/> <separator/> <menuitem action='style-h1'/> <menuitem action='style-h2'/> @@ -164,8 +163,6 @@ </menu> <separator/> <menu action='context-insert-table-menu'> - <menuitem action='context-insert-table'/> - <separator/> <menuitem action='context-insert-row-above'/> <menuitem action='context-insert-row-below'/> <separator/> @@ -178,7 +175,5 @@ <menuitem action='context-delete-column'/> <menuitem action='context-delete-cell'/> </menu> - <separator/> - <menu action='context-input-methods-menu'/> </popup> </ui> diff --git a/e-util/e-html-editor-page-dialog.c b/e-util/e-html-editor-page-dialog.c index 9033ff3..2399566 100644 --- a/e-util/e-html-editor-page-dialog.c +++ b/e-util/e-html-editor-page-dialog.c @@ -44,8 +44,6 @@ struct _EHTMLEditorPageDialogPrivate { GtkWidget *background_image_filechooser; GtkWidget *remove_image_button; - - EHTMLEditorViewHistoryEvent *history_event; }; typedef struct _Template { @@ -150,85 +148,64 @@ static void html_editor_page_dialog_set_text_color (EHTMLEditorPageDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; + EContentEditor *cnt_editor; GdkRGBA rgba; - gchar *color; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); + cnt_editor = e_html_editor_get_content_editor (editor); e_color_combo_get_current_color ( E_COLOR_COMBO (dialog->priv->text_color_picker), &rgba); - color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba)); - webkit_dom_html_body_element_set_text ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body), color); - - g_free (color); + e_content_editor_page_set_text_color (cnt_editor, &rgba); } static void html_editor_page_dialog_set_link_color (EHTMLEditorPageDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GdkRGBA rgba; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); e_color_combo_get_current_color ( E_COLOR_COMBO (dialog->priv->link_color_picker), &rgba); - e_html_editor_view_set_link_color (view, &rgba); + e_content_editor_page_set_link_color (cnt_editor, &rgba); } static void html_editor_page_dialog_set_visited_link_color (EHTMLEditorPageDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GdkRGBA rgba; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); e_color_combo_get_current_color ( E_COLOR_COMBO (dialog->priv->visited_link_color_picker), &rgba); - e_html_editor_view_set_visited_link_color (view, &rgba); + e_content_editor_page_set_visited_link_color (cnt_editor, &rgba); } static void html_editor_page_dialog_set_background_color (EHTMLEditorPageDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; + EContentEditor *cnt_editor; GdkRGBA rgba; - gchar *color; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); + cnt_editor = e_html_editor_get_content_editor (editor); e_color_combo_get_current_color ( E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba); - if (rgba.alpha != 0.0) - color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba)); - else - color = g_strdup (""); - - webkit_dom_html_body_element_set_bg_color ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body), color); - g_free (color); + e_content_editor_page_set_background_color (cnt_editor, &rgba); } static void @@ -271,28 +248,16 @@ static void html_editor_page_dialog_set_background_image (EHTMLEditorPageDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; + EContentEditor *cnt_editor; gchar *uri; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); + cnt_editor = e_html_editor_get_content_editor (editor); uri = gtk_file_chooser_get_uri ( - GTK_FILE_CHOOSER ( - dialog->priv->background_image_filechooser)); - - if (uri && *uri) - e_html_editor_selection_replace_image_src ( - e_html_editor_view_get_selection (view), - WEBKIT_DOM_ELEMENT (body), - uri); - else - remove_image_attributes_from_element ( - WEBKIT_DOM_ELEMENT (body)); + GTK_FILE_CHOOSER (dialog->priv->background_image_filechooser)); + + e_content_editor_page_set_background_image_uri (cnt_editor, uri); gtk_widget_set_sensitive (dialog->priv->remove_image_button, uri && *uri); @@ -303,16 +268,12 @@ static void html_editor_page_dialog_remove_image (EHTMLEditorPageDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; + EContentEditor *cnt_editor; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); + cnt_editor = e_html_editor_get_content_editor (editor); - remove_image_attributes_from_element (WEBKIT_DOM_ELEMENT (body)); + e_content_editor_page_set_background_image_uri (cnt_editor, NULL); gtk_file_chooser_unselect_all ( GTK_FILE_CHOOSER (dialog->priv->background_image_filechooser)); @@ -324,44 +285,21 @@ static void html_editor_page_dialog_show (GtkWidget *widget) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; EHTMLEditorPageDialog *dialog; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - gchar *tmp; GdkRGBA rgba; + gchar *uri; dialog = E_HTML_EDITOR_PAGE_DIALOG (widget); editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); - /* We have to block the style changes of the view as otherwise the colors - * will be changed when this dialog will be shown (as the view will be - * unfocused). */ - e_html_editor_view_block_style_updated_callbacks (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_PAGE_DIALOG; - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - ev->data.dom.from = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE); - dialog->priv->history_event = ev; - } + e_content_editor_on_page_dialog_open (cnt_editor); - tmp = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-uri"); - if (tmp && *tmp) { + uri = e_content_editor_page_get_background_image_uri (cnt_editor); + if (uri && *uri) { gint ii; - gchar *fname = g_filename_from_uri (tmp, NULL, NULL); + gchar *fname = g_filename_from_uri (uri, NULL, NULL); for (ii = 0; ii < G_N_ELEMENTS (templates); ii++) { const Template *tmplt = &templates[ii]; @@ -377,140 +315,39 @@ html_editor_page_dialog_show (GtkWidget *widget) gtk_combo_box_set_active ( GTK_COMBO_BOX (dialog->priv->background_template_combo), 0); } - g_free (tmp); + g_free (uri); - tmp = webkit_dom_html_body_element_get_text ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body)); - if (!tmp || !*tmp || !gdk_rgba_parse (&rgba, tmp)) - e_utils_get_theme_color (widget, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR, &rgba); - g_free (tmp); + e_content_editor_page_get_text_color (cnt_editor, &rgba); e_color_combo_set_current_color ( E_COLOR_COMBO (dialog->priv->text_color_picker), &rgba); - tmp = webkit_dom_html_body_element_get_link ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body)); - if (!gdk_rgba_parse (&rgba, tmp)) { - rgba.alpha = 1; - rgba.red = 0; - rgba.green = 0; - rgba.blue = 1; - } - g_free (tmp); + e_content_editor_page_get_link_color (cnt_editor, &rgba); e_color_combo_set_current_color ( E_COLOR_COMBO (dialog->priv->link_color_picker), &rgba); - tmp = webkit_dom_html_body_element_get_v_link ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body)); - if (!gdk_rgba_parse (&rgba, tmp)) { - rgba.alpha = 1; - rgba.red = 1; - rgba.green = 0; - rgba.blue = 0; - } - g_free (tmp); + e_content_editor_page_get_visited_link_color (cnt_editor, &rgba); e_color_combo_set_current_color ( E_COLOR_COMBO (dialog->priv->visited_link_color_picker), &rgba); - tmp = webkit_dom_html_body_element_get_bg_color ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body)); - if (!tmp || !*tmp || !gdk_rgba_parse (&rgba, tmp)) - e_utils_get_theme_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &rgba); - g_free (tmp); + e_content_editor_page_get_background_color (cnt_editor, &rgba); e_color_combo_set_current_color ( E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba); GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->show (widget); } -static gboolean -user_changed_content (EHTMLEditorViewHistoryEvent *event) -{ - WebKitDOMElement *original, *current; - gchar *original_value, *current_value; - gboolean changed = TRUE; - - original = WEBKIT_DOM_ELEMENT (event->data.dom.from); - current = WEBKIT_DOM_ELEMENT (event->data.dom.to); - - original_value = webkit_dom_element_get_attribute (original, "bgcolor"); - current_value = webkit_dom_element_get_attribute (current, "bgcolor"); - changed = g_strcmp0 (original_value, current_value) != 0; - g_free (original_value); - g_free (current_value); - if (changed) - return TRUE; - - original_value = webkit_dom_element_get_attribute (original, "text"); - current_value = webkit_dom_element_get_attribute (current, "text"); - changed = g_strcmp0 (original_value, current_value) != 0; - g_free (original_value); - g_free (current_value); - if (changed) - return TRUE; - - original_value = webkit_dom_element_get_attribute (original, "link"); - current_value = webkit_dom_element_get_attribute (current, "link"); - changed = g_strcmp0 (original_value, current_value) != 0; - g_free (original_value); - g_free (current_value); - if (changed) - return TRUE; - - original_value = webkit_dom_element_get_attribute (original, "vlink"); - current_value = webkit_dom_element_get_attribute (current, "vlink"); - changed = g_strcmp0 (original_value, current_value) != 0; - g_free (original_value); - g_free (current_value); - - return changed; -} - static void html_editor_page_dialog_hide (GtkWidget *widget) { - EHTMLEditorPageDialogPrivate *priv; - EHTMLEditorViewHistoryEvent *ev; - EHTMLEditorPageDialog *dialog; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; + EHTMLEditorPageDialog *dialog; dialog = E_HTML_EDITOR_PAGE_DIALOG (widget); editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - priv = E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE (widget); - ev = priv->history_event; - - if (ev) { - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - ev->data.dom.to = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE); - - /* If user changed any of page colors we have to mark it to send - * the correct colors and to disable the color changes when the - * view i.e. not focused (at it would overwrite these user set colors. */ - if (user_changed_content (ev)) - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-user-colors", "", NULL); - - if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) { - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } else { - g_object_unref (ev->data.dom.from); - g_object_unref (ev->data.dom.to); - g_free (ev); - } - } + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_view_unblock_style_updated_callbacks (view); + e_content_editor_on_page_dialog_close (cnt_editor); GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->hide (widget); } diff --git a/e-util/e-html-editor-private.h b/e-util/e-html-editor-private.h index 60f6706..37523a5 100644 --- a/e-util/e-html-editor-private.h +++ b/e-util/e-html-editor-private.h @@ -36,7 +36,6 @@ #include <e-html-editor-spell-check-dialog.h> #include <e-html-editor-table-dialog.h> #include <e-html-editor-text-dialog.h> -#include <e-html-editor-view.h> #ifdef HAVE_XFREE #include <X11/XF86keysym.h> @@ -84,21 +83,24 @@ struct _EHTMLEditorPrivate { GtkWidget *style_combo_box; GtkWidget *scrolled_window; - EHTMLEditorView *html_editor_view; - EHTMLEditorSelection *selection; + GHashTable *content_editors; + EContentEditor *use_content_editor; + + EContentEditorNodeFlags node_flags; gchar *filename; guint spell_suggestions_merge_id; - WebKitDOMNode *image; - WebKitDOMNode *table_cell; - WebKitDOMNode *current_node; - gint editor_layout_row; + + gboolean is_testing; }; void editor_actions_init (EHTMLEditor *editor); +void editor_actions_bind (EHTMLEditor *editor); +const gchar * e_html_editor_get_content_editor_name + (EHTMLEditor *editor); G_END_DECLS diff --git a/e-util/e-html-editor-replace-dialog.c b/e-util/e-html-editor-replace-dialog.c index d21bef2..59b69a7 100644 --- a/e-util/e-html-editor-replace-dialog.c +++ b/e-util/e-html-editor-replace-dialog.c @@ -49,7 +49,9 @@ struct _EHTMLEditorReplaceDialogPrivate { GtkWidget *replace_button; GtkWidget *replace_all_button; - EHTMLEditor *editor; + EContentEditor *cnt_editor; + gulong find_done_handler_id; + gulong replace_all_done_handler_id; }; enum { @@ -57,130 +59,111 @@ enum { PROP_EDITOR }; -static gboolean -jump (EHTMLEditorReplaceDialog *dialog) +static void +content_editor_find_done_cb (EContentEditor *cnt_editor, + guint match_count, + EHTMLEditorReplaceDialog *dialog) { - EHTMLEditor *editor; - WebKitWebView *web_view; - gboolean found; + if (match_count) { + gtk_widget_hide (dialog->priv->result_label); + } else { + gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), _("No match found")); + gtk_widget_show (dialog->priv->result_label); + } +} - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - web_view = WEBKIT_WEB_VIEW ( - e_html_editor_get_view (editor)); +static void +replace_occurance (EHTMLEditorReplaceDialog *dialog) +{ + gtk_widget_hide (dialog->priv->result_label); - found = webkit_web_view_search_text ( - web_view, - gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)), - gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive)), - !gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (dialog->priv->backwards)), - gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (dialog->priv->wrap))); - - return found; + e_content_editor_replace ( + dialog->priv->cnt_editor, + gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry))); } static void -html_editor_replace_dialog_skip_cb (EHTMLEditorReplaceDialog *dialog) +content_editor_replace_all_done_cb (EContentEditor *cnt_editor, + guint replaced_count, + EHTMLEditorReplaceDialog *dialog) { - if (!jump (dialog)) { - gtk_label_set_label ( - GTK_LABEL (dialog->priv->result_label), - N_("No match found")); - gtk_widget_show (dialog->priv->result_label); - } else { - gtk_widget_hide (dialog->priv->result_label); - } + gchar *result; + + result = g_strdup_printf (ngettext("%d occurrence replaced", + "%d occurrences replaced", + replaced_count), + replaced_count); + + gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), result); + gtk_widget_show (dialog->priv->result_label); + g_free (result); } -static void -html_editor_replace_dialog_replace_cb (EHTMLEditorReplaceDialog *dialog) +static guint32 +replace_dialog_get_find_flags (EHTMLEditorReplaceDialog *dialog) { - EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + guint32 flags = E_CONTENT_EDITOR_FIND_NEXT; - /* Jump to next matching word */ - if (!jump (dialog)) { - gtk_label_set_label ( - GTK_LABEL (dialog->priv->result_label), - N_("No match found")); - gtk_widget_show (dialog->priv->result_label); - return; - } else { - gtk_widget_hide (dialog->priv->result_label); - } + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->backwards))) + flags |= E_CONTENT_EDITOR_FIND_MODE_BACKWARDS; - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive))) + flags |= E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE; - e_html_editor_selection_replace ( - selection, - gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry))); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->wrap))) + flags |= E_CONTENT_EDITOR_FIND_WRAP_AROUND; + + return flags; } static void -html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog) +search (EHTMLEditorReplaceDialog *dialog) { - gint i = 0; - gchar *result; - EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - EHTMLEditorSelection *selection; - const gchar *replacement, *search_text; - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - replacement = gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry)); - search_text = gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)); - - while (jump (dialog)) { - e_html_editor_selection_replace (selection, replacement); - i++; - - /* Jump behind the word */ - e_html_editor_selection_move ( - selection, TRUE, E_HTML_EDITOR_SELECTION_GRANULARITY_WORD); - } + e_content_editor_find ( + dialog->priv->cnt_editor, + replace_dialog_get_find_flags (dialog), + gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry))); +} - if (i != 0) { - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_REPLACE_ALL; +static void +html_editor_replace_dialog_skip_cb (EHTMLEditorReplaceDialog *dialog) +{ + search (dialog); +} - ev->data.string.from = g_strdup (search_text); - ev->data.string.to = g_strdup (replacement); +static void +html_editor_replace_dialog_replace_cb (EHTMLEditorReplaceDialog *dialog) +{ + replace_occurance (dialog); - e_html_editor_view_insert_new_history_event (view, ev); - } - e_html_editor_view_force_spell_check_in_viewport (view); - } + /* Jump to next matching word */ + search (dialog); +} - result = g_strdup_printf (ngettext("%d occurrence replaced", - "%d occurrences replaced", - i), - i); - gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), result); - gtk_widget_show (dialog->priv->result_label); - g_free (result); +static void +html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog) +{ + e_content_editor_replace_all ( + dialog->priv->cnt_editor, + replace_dialog_get_find_flags (dialog), + gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)), + gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry))); } static void html_editor_replace_dialog_entry_changed (EHTMLEditorReplaceDialog *dialog) { gboolean ready; - ready = ((gtk_entry_get_text_length ( - GTK_ENTRY (dialog->priv->search_entry)) != 0) && - (gtk_entry_get_text_length ( - GTK_ENTRY (dialog->priv->replace_entry)) != 0)); + + ready = gtk_entry_get_text_length (GTK_ENTRY (dialog->priv->search_entry)) != 0; gtk_widget_set_sensitive (dialog->priv->skip_button, ready); gtk_widget_set_sensitive (dialog->priv->replace_button, ready); gtk_widget_set_sensitive (dialog->priv->replace_all_button, ready); + + if (ready) + search (dialog); } static void @@ -188,6 +171,8 @@ html_editor_replace_dialog_show (GtkWidget *widget) { EHTMLEditorReplaceDialog *dialog = E_HTML_EDITOR_REPLACE_DIALOG (widget); + e_content_editor_on_replace_dialog_open (dialog->priv->cnt_editor); + gtk_widget_grab_focus (dialog->priv->search_entry); gtk_widget_hide (dialog->priv->result_label); @@ -196,14 +181,82 @@ html_editor_replace_dialog_show (GtkWidget *widget) } static void +html_editor_replace_dialog_hide (GtkWidget *widget) +{ + EHTMLEditorReplaceDialog *dialog = E_HTML_EDITOR_REPLACE_DIALOG (widget); + + e_content_editor_on_replace_dialog_close (dialog->priv->cnt_editor); + + /* Chain up to parent implementation */ + GTK_WIDGET_CLASS (e_html_editor_replace_dialog_parent_class)->hide (widget); +} + +static void +html_editor_replace_dialog_constructed (GObject *object) +{ + EContentEditor *cnt_editor; + EHTMLEditor *editor; + EHTMLEditorReplaceDialog *dialog; + + dialog = E_HTML_EDITOR_REPLACE_DIALOG (object); + dialog->priv = E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE (dialog); + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + dialog->priv->find_done_handler_id = g_signal_connect ( + cnt_editor, "find-done", + G_CALLBACK (content_editor_find_done_cb), dialog); + + dialog->priv->replace_all_done_handler_id = g_signal_connect ( + cnt_editor, "replace-all-done", + G_CALLBACK (content_editor_replace_all_done_cb), dialog); + + dialog->priv->cnt_editor = cnt_editor; + + G_OBJECT_CLASS (e_html_editor_replace_dialog_parent_class)->constructed (object); +} + +static void +html_editor_replace_dialog_dispose (GObject *object) +{ + EHTMLEditorReplaceDialogPrivate *priv; + + priv = E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE (object); + + if (priv->find_done_handler_id > 0) { + g_signal_handler_disconnect ( + priv->cnt_editor, + priv->find_done_handler_id); + priv->find_done_handler_id = 0; + } + + if (priv->replace_all_done_handler_id > 0) { + g_signal_handler_disconnect ( + priv->cnt_editor, + priv->replace_all_done_handler_id); + priv->replace_all_done_handler_id = 0; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_html_editor_replace_dialog_parent_class)->dispose (object); +} + +static void e_html_editor_replace_dialog_class_init (EHTMLEditorReplaceDialogClass *class) { + GObjectClass *object_class; GtkWidgetClass *widget_class; g_type_class_add_private (class, sizeof (EHTMLEditorReplaceDialogPrivate)); + object_class = G_OBJECT_CLASS (class); + object_class->constructed = html_editor_replace_dialog_constructed; + object_class->dispose = html_editor_replace_dialog_dispose; + widget_class = GTK_WIDGET_CLASS (class); widget_class->show = html_editor_replace_dialog_show; + widget_class->hide = html_editor_replace_dialog_hide; } static void @@ -232,9 +285,6 @@ e_html_editor_replace_dialog_init (EHTMLEditorReplaceDialog *dialog) widget = gtk_entry_new (); gtk_grid_attach (main_layout, widget, 1, 1, 2, 1); dialog->priv->replace_entry = widget; - g_signal_connect_swapped ( - widget, "notify::text-length", - G_CALLBACK (html_editor_replace_dialog_entry_changed), dialog); widget = gtk_label_new_with_mnemonic (_("_With:")); gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->replace_entry); diff --git a/e-util/e-html-editor-selection.c b/e-util/e-html-editor-selection.c deleted file mode 100644 index bb7e145..0000000 --- a/e-util/e-html-editor-selection.c +++ /dev/null @@ -1,8120 +0,0 @@ -/* - * e-html-editor-selection.c - * - * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com> - * - * 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 <http://www.gnu.org/licenses/> - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "e-html-editor-selection.h" -#include "e-html-editor-view.h" -#include "e-html-editor.h" -#include "e-html-editor-utils.h" - -#include <e-util/e-util.h> - -#include <webkit/webkit.h> -#include <webkit/webkitdom.h> -#include <string.h> -#include <stdlib.h> -#include <ctype.h> - -#define E_HTML_EDITOR_SELECTION_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionPrivate)) - -/** - * EHTMLEditorSelection - * - * The #EHTMLEditorSelection object represents current position of the cursor - * with the editor or current text selection within the editor. To obtain - * valid #EHTMLEditorSelection, call e_html_editor_view_get_selection(). - */ - -struct _EHTMLEditorSelectionPrivate { - - GWeakRef html_editor_view; - gulong selection_changed_handler_id; - gboolean selection_changed_callbacks_blocked; - - gchar *text; - - gboolean is_bold; - gboolean is_italic; - gboolean is_underline; - gboolean is_monospaced; - gboolean is_strikethrough; - - gchar *background_color; - gchar *font_color; - gchar *font_family; - - gulong selection_offset; - - gint word_wrap_length; - guint font_size; - - EHTMLEditorSelectionAlignment alignment; -}; - -enum { - PROP_0, - PROP_ALIGNMENT, - PROP_BACKGROUND_COLOR, - PROP_BLOCK_FORMAT, - PROP_BOLD, - PROP_HTML_EDITOR_VIEW, - PROP_FONT_COLOR, - PROP_FONT_NAME, - PROP_FONT_SIZE, - PROP_INDENTED, - PROP_ITALIC, - PROP_MONOSPACED, - PROP_STRIKETHROUGH, - PROP_SUBSCRIPT, - PROP_SUPERSCRIPT, - PROP_TEXT, - PROP_UNDERLINE -}; - -static const GdkRGBA black = { 0, 0, 0, 1 }; - -G_DEFINE_TYPE ( - EHTMLEditorSelection, - e_html_editor_selection, - G_TYPE_OBJECT -); - -static WebKitDOMRange * -html_editor_selection_get_current_range (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range = NULL; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_val_if_fail (view != NULL, NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - g_object_unref (view); - dom_window = webkit_dom_document_get_default_view (document); - if (!dom_window) - return NULL; - - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - if (!dom_selection) { - g_object_unref (dom_window); - return NULL; - } - - if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) - goto exit; - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - exit: - g_object_unref (dom_selection); - g_object_unref (dom_window); - - return range; -} - -static gboolean -get_has_style (EHTMLEditorSelection *selection, - const gchar *style_tag) -{ - WebKitDOMNode *node; - WebKitDOMElement *element; - WebKitDOMRange *range; - gboolean result; - gint tag_len; - - range = html_editor_selection_get_current_range (selection); - if (!range) - return FALSE; - - node = webkit_dom_range_get_start_container (range, NULL); - if (WEBKIT_DOM_IS_ELEMENT (node)) - element = WEBKIT_DOM_ELEMENT (node); - else - element = webkit_dom_node_get_parent_element (node); - g_object_unref (range); - - tag_len = strlen (style_tag); - result = FALSE; - while (!result && element) { - gchar *element_tag; - gboolean accept_citation = FALSE; - - element_tag = webkit_dom_element_get_tag_name (element); - - if (g_ascii_strncasecmp (style_tag, "citation", 8) == 0) { - accept_citation = TRUE; - result = WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element); - if (element_has_class (element, "-x-evo-indented")) - result = FALSE; - } else { - result = ((tag_len == strlen (element_tag)) && - (g_ascii_strncasecmp (element_tag, style_tag, tag_len) == 0)); - } - - /* Special case: <blockquote type=cite> marks quotation, while - * just <blockquote> is used for indentation. If the <blockquote> - * has type=cite, then ignore it unless style_tag is "citation" */ - if (result && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element)) { - if (webkit_dom_element_has_attribute (element, "type")) { - gchar *type; - type = webkit_dom_element_get_attribute (element, "type"); - if (!accept_citation && (g_ascii_strncasecmp (type, "cite", 4) == 0)) { - result = FALSE; - } - g_free (type); - } else { - if (accept_citation) - result = FALSE; - } - } - - g_free (element_tag); - - if (result) - break; - - element = webkit_dom_node_get_parent_element ( - WEBKIT_DOM_NODE (element)); - } - - return result; -} - -static gchar * -get_font_property (EHTMLEditorSelection *selection, - const gchar *font_property) -{ - WebKitDOMRange *range; - WebKitDOMNode *node; - WebKitDOMElement *element; - gchar *value; - - range = html_editor_selection_get_current_range (selection); - if (!range) - return NULL; - - node = webkit_dom_range_get_common_ancestor_container (range, NULL); - g_object_unref (range); - element = e_html_editor_dom_node_find_parent_element (node, "FONT"); - while (element && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (element) && - !webkit_dom_element_has_attribute (element, font_property)) { - element = e_html_editor_dom_node_find_parent_element ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), "FONT"); - } - - if (!element) - return NULL; - - g_object_get (G_OBJECT (element), font_property, &value, NULL); - - return value; -} - -static void -html_editor_selection_selection_changed_cb (WebKitWebView *web_view, - EHTMLEditorSelection *selection) -{ - WebKitDOMRange *range = NULL; - - range = html_editor_selection_get_current_range (selection); - if (!range) - return; - g_object_unref (range); - - g_object_freeze_notify (G_OBJECT (selection)); - - g_object_notify (G_OBJECT (selection), "alignment"); - g_object_notify (G_OBJECT (selection), "block-format"); - g_object_notify (G_OBJECT (selection), "indented"); - g_object_notify (G_OBJECT (selection), "text"); - - if (!e_html_editor_view_get_html_mode (E_HTML_EDITOR_VIEW (web_view))) - goto out; - - g_object_notify (G_OBJECT (selection), "background-color"); - g_object_notify (G_OBJECT (selection), "bold"); - g_object_notify (G_OBJECT (selection), "font-name"); - g_object_notify (G_OBJECT (selection), "font-size"); - g_object_notify (G_OBJECT (selection), "font-color"); - g_object_notify (G_OBJECT (selection), "italic"); - g_object_notify (G_OBJECT (selection), "monospaced"); - g_object_notify (G_OBJECT (selection), "strikethrough"); - g_object_notify (G_OBJECT (selection), "subscript"); - g_object_notify (G_OBJECT (selection), "superscript"); - g_object_notify (G_OBJECT (selection), "underline"); - - out: - g_object_thaw_notify (G_OBJECT (selection)); -} - -void -e_html_editor_selection_block_selection_changed (EHTMLEditorSelection *selection) -{ - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (!selection->priv->selection_changed_callbacks_blocked) { - EHTMLEditorView *view; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_signal_handlers_block_by_func ( - view, html_editor_selection_selection_changed_cb, selection); - g_object_unref (view); - selection->priv->selection_changed_callbacks_blocked = TRUE; - } -} - -void -e_html_editor_selection_unblock_selection_changed (EHTMLEditorSelection *selection) -{ - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (selection->priv->selection_changed_callbacks_blocked) { - EHTMLEditorView *view; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_signal_handlers_unblock_by_func ( - view, html_editor_selection_selection_changed_cb, selection); - g_object_unref (view); - selection->priv->selection_changed_callbacks_blocked = FALSE; - - html_editor_selection_selection_changed_cb (WEBKIT_WEB_VIEW (view), selection); - } -} - -static void -html_editor_selection_set_html_editor_view (EHTMLEditorSelection *selection, - EHTMLEditorView *view) -{ - gulong handler_id; - - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - g_weak_ref_set (&selection->priv->html_editor_view, view); - - handler_id = g_signal_connect ( - view, "selection-changed", - G_CALLBACK (html_editor_selection_selection_changed_cb), - selection); - - selection->priv->selection_changed_handler_id = handler_id; -} - -static void -html_editor_selection_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GdkRGBA rgba = { 0 }; - - switch (property_id) { - case PROP_ALIGNMENT: - g_value_set_int ( - value, - e_html_editor_selection_get_alignment ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_BACKGROUND_COLOR: - g_value_set_string ( - value, - e_html_editor_selection_get_background_color ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_BLOCK_FORMAT: - g_value_set_int ( - value, - e_html_editor_selection_get_block_format ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_BOLD: - g_value_set_boolean ( - value, - e_html_editor_selection_is_bold ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_HTML_EDITOR_VIEW: - g_value_take_object ( - value, - e_html_editor_selection_ref_html_editor_view ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_FONT_COLOR: - e_html_editor_selection_get_font_color ( - E_HTML_EDITOR_SELECTION (object), &rgba); - g_value_set_boxed (value, &rgba); - return; - - case PROP_FONT_NAME: - g_value_set_string ( - value, - e_html_editor_selection_get_font_name ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_FONT_SIZE: - g_value_set_int ( - value, - e_html_editor_selection_get_font_size ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_INDENTED: - g_value_set_boolean ( - value, - e_html_editor_selection_is_indented ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_ITALIC: - g_value_set_boolean ( - value, - e_html_editor_selection_is_italic ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_MONOSPACED: - g_value_set_boolean ( - value, - e_html_editor_selection_is_monospaced ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_STRIKETHROUGH: - g_value_set_boolean ( - value, - e_html_editor_selection_is_strikethrough ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_SUBSCRIPT: - g_value_set_boolean ( - value, - e_html_editor_selection_is_subscript ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_SUPERSCRIPT: - g_value_set_boolean ( - value, - e_html_editor_selection_is_superscript ( - E_HTML_EDITOR_SELECTION (object))); - return; - - case PROP_TEXT: - g_value_set_string ( - value, - e_html_editor_selection_get_string ( - E_HTML_EDITOR_SELECTION (object))); - break; - - case PROP_UNDERLINE: - g_value_set_boolean ( - value, - e_html_editor_selection_is_underline ( - E_HTML_EDITOR_SELECTION (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -html_editor_selection_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_ALIGNMENT: - e_html_editor_selection_set_alignment ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_int (value)); - return; - - case PROP_BACKGROUND_COLOR: - e_html_editor_selection_set_background_color ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_string (value)); - return; - - case PROP_BOLD: - e_html_editor_selection_set_bold ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_boolean (value)); - return; - - case PROP_HTML_EDITOR_VIEW: - html_editor_selection_set_html_editor_view ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_object (value)); - return; - - case PROP_FONT_COLOR: - e_html_editor_selection_set_font_color ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_boxed (value)); - return; - - case PROP_BLOCK_FORMAT: - e_html_editor_selection_set_block_format ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_int (value)); - return; - - case PROP_FONT_NAME: - e_html_editor_selection_set_font_name ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_string (value)); - return; - - case PROP_FONT_SIZE: - e_html_editor_selection_set_font_size ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_int (value)); - return; - - case PROP_ITALIC: - e_html_editor_selection_set_italic ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_boolean (value)); - return; - - case PROP_MONOSPACED: - e_html_editor_selection_set_monospaced ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_boolean (value)); - return; - - case PROP_STRIKETHROUGH: - e_html_editor_selection_set_strikethrough ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_boolean (value)); - return; - - case PROP_SUBSCRIPT: - e_html_editor_selection_set_subscript ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_boolean (value)); - return; - - case PROP_SUPERSCRIPT: - e_html_editor_selection_set_superscript ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_boolean (value)); - return; - - case PROP_UNDERLINE: - e_html_editor_selection_set_underline ( - E_HTML_EDITOR_SELECTION (object), - g_value_get_boolean (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -html_editor_selection_dispose (GObject *object) -{ - EHTMLEditorSelectionPrivate *priv; - EHTMLEditorView *view; - - priv = E_HTML_EDITOR_SELECTION_GET_PRIVATE (object); - - view = g_weak_ref_get (&priv->html_editor_view); - if (view != NULL) { - g_signal_handler_disconnect ( - view, priv->selection_changed_handler_id); - priv->selection_changed_handler_id = 0; - g_object_unref (view); - } - - g_weak_ref_set (&priv->html_editor_view, NULL); - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_html_editor_selection_parent_class)->dispose (object); -} - -static void -html_editor_selection_finalize (GObject *object) -{ - EHTMLEditorSelection *selection = E_HTML_EDITOR_SELECTION (object); - - g_free (selection->priv->text); - g_free (selection->priv->background_color); - g_free (selection->priv->font_color); - g_free (selection->priv->font_family); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_html_editor_selection_parent_class)->finalize (object); -} - -static void -e_html_editor_selection_class_init (EHTMLEditorSelectionClass *class) -{ - GObjectClass *object_class; - - g_type_class_add_private (class, sizeof (EHTMLEditorSelectionPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->get_property = html_editor_selection_get_property; - object_class->set_property = html_editor_selection_set_property; - object_class->dispose = html_editor_selection_dispose; - object_class->finalize = html_editor_selection_finalize; - - /** - * EHTMLEditorSelection:alignment - * - * Holds alignment of current paragraph. - */ - /* FIXME: Convert the enum to a proper type */ - g_object_class_install_property ( - object_class, - PROP_ALIGNMENT, - g_param_spec_int ( - "alignment", - NULL, - NULL, - E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT, - E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT, - E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT, - G_PARAM_READWRITE)); - - /** - * EHTMLEditorSelection:background-color - * - * Holds background color of current selection or at current cursor - * position. - */ - g_object_class_install_property ( - object_class, - PROP_BACKGROUND_COLOR, - g_param_spec_string ( - "background-color", - NULL, - NULL, - NULL, - G_PARAM_READWRITE)); - - /** - * EHTMLEditorSelection:block-format - * - * Holds block format of current paragraph. See - * #EHTMLEditorSelectionBlockFormat for valid values. - */ - /* FIXME Convert the EHTMLEditorSelectionBlockFormat - * enum to a proper type. */ - g_object_class_install_property ( - object_class, - PROP_BLOCK_FORMAT, - g_param_spec_int ( - "block-format", - NULL, - NULL, - 0, - G_MAXINT, - 0, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:bold - * - * Holds whether current selection or text at current cursor position - * is bold. - */ - g_object_class_install_property ( - object_class, - PROP_BOLD, - g_param_spec_boolean ( - "bold", - NULL, - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_HTML_EDITOR_VIEW, - g_param_spec_object ( - "html-editor-view", - NULL, - NULL, - E_TYPE_HTML_EDITOR_VIEW, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:font-color - * - * Holds font color of current selection or at current cursor position. - */ - g_object_class_install_property ( - object_class, - PROP_FONT_COLOR, - g_param_spec_boxed ( - "font-color", - NULL, - NULL, - GDK_TYPE_RGBA, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:font-name - * - * Holds name of font in current selection or at current cursor - * position. - */ - g_object_class_install_property ( - object_class, - PROP_FONT_NAME, - g_param_spec_string ( - "font-name", - NULL, - NULL, - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:font-size - * - * Holds point size of current selection or at current cursor position. - */ - g_object_class_install_property ( - object_class, - PROP_FONT_SIZE, - g_param_spec_int ( - "font-size", - NULL, - NULL, - 1, - 7, - 3, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:indented - * - * Holds whether current paragraph is indented. This does not include - * citations. - */ - g_object_class_install_property ( - object_class, - PROP_INDENTED, - g_param_spec_boolean ( - "indented", - NULL, - NULL, - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:italic - * - * Holds whether current selection or letter at current cursor position - * is italic. - */ - g_object_class_install_property ( - object_class, - PROP_ITALIC, - g_param_spec_boolean ( - "italic", - NULL, - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:monospaced - * - * Holds whether current selection or letter at current cursor position - * is monospaced. - */ - g_object_class_install_property ( - object_class, - PROP_MONOSPACED, - g_param_spec_boolean ( - "monospaced", - NULL, - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:strikethrough - * - * Holds whether current selection or letter at current cursor position - * is strikethrough. - */ - g_object_class_install_property ( - object_class, - PROP_STRIKETHROUGH, - g_param_spec_boolean ( - "strikethrough", - NULL, - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:superscript - * - * Holds whether current selection or letter at current cursor position - * is in superscript. - */ - g_object_class_install_property ( - object_class, - PROP_SUPERSCRIPT, - g_param_spec_boolean ( - "superscript", - NULL, - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:subscript - * - * Holds whether current selection or letter at current cursor position - * is in subscript. - */ - g_object_class_install_property ( - object_class, - PROP_SUBSCRIPT, - g_param_spec_boolean ( - "subscript", - NULL, - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:text - * - * Holds always up-to-date text of current selection. - */ - g_object_class_install_property ( - object_class, - PROP_TEXT, - g_param_spec_string ( - "text", - NULL, - NULL, - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorSelection:underline - * - * Holds whether current selection or letter at current cursor position - * is underlined. - */ - g_object_class_install_property ( - object_class, - PROP_UNDERLINE, - g_param_spec_boolean ( - "underline", - NULL, - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); -} - -static void -e_html_editor_selection_init (EHTMLEditorSelection *selection) -{ - GSettings *g_settings; - - selection->priv = E_HTML_EDITOR_SELECTION_GET_PRIVATE (selection); - - g_settings = e_util_ref_settings ("org.gnome.evolution.mail"); - selection->priv->word_wrap_length = - g_settings_get_int (g_settings, "composer-word-wrap-length"); - g_object_unref (g_settings); - - selection->priv->selection_changed_callbacks_blocked = FALSE; -} - -gint -e_html_editor_selection_get_word_wrap_length (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), 72); - - return selection->priv->word_wrap_length; -} - -/** - * e_html_editor_selection_ref_html_editor_view: - * @selection: an #EHTMLEditorSelection - * - * Returns a new reference to @selection's #EHTMLEditorView. Unreference - * the #EHTMLEditorView with g_object_unref() when finished with it. - * - * Returns: an #EHTMLEditorView - **/ -EHTMLEditorView * -e_html_editor_selection_ref_html_editor_view (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL); - - return g_weak_ref_get (&selection->priv->html_editor_view); -} - -/** - * e_html_editor_selection_has_text: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current selection contains any text. - * - * Returns: @TRUE when current selection contains text, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_has_text (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - gboolean has_text = FALSE; - gchar *text = NULL; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window = NULL; - WebKitDOMDOMSelection *dom_selection = NULL; - WebKitDOMRange *range = NULL; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_val_if_fail (view != NULL, FALSE); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - g_object_unref (view); - - if (!(dom_window = webkit_dom_document_get_default_view (document))) - goto out; - - if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) - goto out; - - if (webkit_dom_dom_selection_get_is_collapsed (dom_selection)) - goto out; - - if (!(range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL))) - goto out; - - text = webkit_dom_range_get_text (range); - has_text = text && *text; - out: - g_free (text); - g_clear_object (&dom_window); - g_clear_object (&dom_selection); - g_clear_object (&range); - - return has_text; -} - -/** - * e_html_editor_selection_get_caret_word: - * @selection: an #EHTMLEditorSelection - * - * Returns word under cursor. - * - * Returns: A newly allocated string with current caret word or @NULL when there - * is no text under cursor or when selection is active. [transfer-full]. - */ -gchar * -e_html_editor_selection_get_caret_word (EHTMLEditorSelection *selection) -{ - gchar *word; - WebKitDOMRange *range; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL); - - range = html_editor_selection_get_current_range (selection); - - /* Don't operate on the visible selection */ - range = webkit_dom_range_clone_range (range, NULL); - webkit_dom_range_expand (range, "word", NULL); - word = webkit_dom_range_to_string (range, NULL); - - g_object_unref (range); - - return word; -} - -/** - * e_html_editor_selection_replace_caret_word: - * @selection: an #EHTMLEditorSelection - * @replacement: a string to replace current caret word with - * - * Replaces current word under cursor with @replacement. - */ -void -e_html_editor_selection_replace_caret_word (EHTMLEditorSelection *selection, - const gchar *replacement) -{ - EHTMLEditorView *view; - WebKitWebView *web_view; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMNode *node; - WebKitDOMRange *range; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (replacement != NULL); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - web_view = WEBKIT_WEB_VIEW (view); - - range = html_editor_selection_get_current_range (selection); - document = webkit_web_view_get_dom_document (web_view); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - webkit_dom_range_expand (range, "word", NULL); - webkit_dom_dom_selection_add_range (dom_selection, range); - - fragment = webkit_dom_range_extract_contents (range, NULL); - - /* Get the text node to replace and leave other formatting nodes - * untouched (font color, boldness, ...). */ - webkit_dom_node_normalize (WEBKIT_DOM_NODE (fragment)); - node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); - if (!WEBKIT_DOM_IS_TEXT (node)) { - while (node && WEBKIT_DOM_IS_ELEMENT (node)) - node = webkit_dom_node_get_first_child (node); - } - - if (node && WEBKIT_DOM_IS_TEXT (node)) { - WebKitDOMText *text; - - /* Replace the word */ - text = webkit_dom_document_create_text_node (document, replacement); - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (text), - node, - NULL); - - /* Insert the word on current location. */ - webkit_dom_range_insert_node (range, WEBKIT_DOM_NODE (fragment), NULL); - - webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); - } - - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - g_object_unref (range); - g_object_unref (dom_selection); - g_object_unref (dom_window); - g_object_unref (view); -} - -/** - * e_html_editor_selection_is_collapsed: - * @selection: an #EHTMLEditorSelection - * - * Returns if selection is collapsed. - * - * Returns: Whether the selection is collapsed (just caret) or not (someting is selected). - */ -gboolean -e_html_editor_selection_is_collapsed (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - gboolean collapsed; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), TRUE); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_val_if_fail (view != NULL, TRUE); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - g_object_unref (view); - if (!(dom_window = webkit_dom_document_get_default_view (document))) - return FALSE; - - if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) { - g_object_unref (dom_window); - return FALSE; - } - - collapsed = webkit_dom_dom_selection_get_is_collapsed (dom_selection); - - g_object_unref (dom_selection); - - return collapsed; -} - -/** - * e_html_editor_selection_get_string: - * @selection: an #EHTMLEditorSelection - * - * Returns currently selected string. - * - * Returns: A pointer to content of current selection. The string is owned by - * #EHTMLEditorSelection and should not be free'd. - */ -const gchar * -e_html_editor_selection_get_string (EHTMLEditorSelection *selection) -{ - WebKitDOMRange *range; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL); - - range = html_editor_selection_get_current_range (selection); - if (!range) - return NULL; - - g_free (selection->priv->text); - selection->priv->text = webkit_dom_range_get_text (range); - - g_object_unref (range); - - return selection->priv->text; -} - -/** - * e_html_editor_selection_replace: - * @selection: an #EHTMLEditorSelection - * @new_string: a string to replace current selection with - * - * Replaces currently selected text with @new_string. - */ -void -e_html_editor_selection_replace (EHTMLEditorSelection *selection, - const gchar *new_string) -{ - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_REPLACE; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - ev->data.string.from = webkit_dom_range_get_text (range); - ev->data.string.to = g_strdup (new_string); - g_object_unref (dom_selection); - g_object_unref (dom_window); - g_object_unref (range); - } - - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, new_string); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - g_object_unref (view); -} - -/** - * e_html_editor_selection_get_list_alignment_from_node: - * @node: #an WebKitDOMNode - * - * Returns alignment of given list. - * - * Returns: #EHTMLEditorSelectionAlignment - */ -EHTMLEditorSelectionAlignment -e_html_editor_selection_get_list_alignment_from_node (WebKitDOMNode *node) -{ - if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-center")) - return E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER; - if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-right")) - return E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT; - else - return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT; -} - -static EHTMLEditorSelectionAlignment -e_html_editor_selection_get_alignment_from_node (WebKitDOMNode *node) -{ - EHTMLEditorSelectionAlignment alignment; - gchar *value; - WebKitDOMCSSStyleDeclaration *style; - - style = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node)); - value = webkit_dom_css_style_declaration_get_property_value (style, "text-align"); - - if (!value || !*value || - (g_ascii_strncasecmp (value, "left", 4) == 0)) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT; - } else if (g_ascii_strncasecmp (value, "center", 6) == 0) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER; - } else if (g_ascii_strncasecmp (value, "right", 5) == 0) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT; - } else { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT; - } - - g_object_unref (style); - - g_free (value); - - return alignment; -} - -/** - * e_html_editor_selection_get_alignment: - * @selection: #an EHTMLEditorSelection - * - * Returns alignment of current paragraph - * - * Returns: #EHTMLEditorSelectionAlignment - */ -EHTMLEditorSelectionAlignment -e_html_editor_selection_get_alignment (EHTMLEditorSelection *selection) -{ - EHTMLEditorSelectionAlignment alignment; - gchar *value; - WebKitDOMCSSStyleDeclaration *style; - WebKitDOMElement *element; - WebKitDOMNode *node; - WebKitDOMRange *range; - - g_return_val_if_fail ( - E_IS_HTML_EDITOR_SELECTION (selection), - E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT); - - range = html_editor_selection_get_current_range (selection); - if (!range) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT; - goto out; - } - - node = webkit_dom_range_get_start_container (range, NULL); - g_object_unref (range); - if (!node) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT; - goto out; - } - - if (WEBKIT_DOM_IS_ELEMENT (node)) - element = WEBKIT_DOM_ELEMENT (node); - else - element = WEBKIT_DOM_ELEMENT (e_html_editor_get_parent_block_node_from_child (node)); - - if (element_has_class (element, "-x-evo-align-right")) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT; - goto out; - } else if (element_has_class (element, "-x-evo-align-center")) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER; - goto out; - } - - style = webkit_dom_element_get_style (element); - value = webkit_dom_css_style_declaration_get_property_value (style, "text-align"); - - if (!value || !*value || - (g_ascii_strncasecmp (value, "left", 4) == 0)) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT; - } else if (g_ascii_strncasecmp (value, "center", 6) == 0) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER; - } else if (g_ascii_strncasecmp (value, "right", 5) == 0) { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT; - } else { - alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT; - } - - g_object_unref (style); - g_free (value); - - out: - selection->priv->alignment = alignment; - - return alignment; -} - -static void -set_ordered_list_type_to_element (WebKitDOMElement *list, - EHTMLEditorSelectionBlockFormat format) -{ - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST) - webkit_dom_element_remove_attribute (list, "type"); - else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA) - webkit_dom_element_set_attribute (list, "type", "A", NULL); - else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN) - webkit_dom_element_set_attribute (list, "type", "I", NULL); -} - -static WebKitDOMElement * -create_list_element (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - EHTMLEditorSelectionBlockFormat format, - gint level, - gboolean html_mode) -{ - gboolean inserting_unordered_list; - WebKitDOMElement *list; - - inserting_unordered_list = format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST; - - list = webkit_dom_document_create_element ( - document, inserting_unordered_list ? "UL" : "OL", NULL); - - if (!inserting_unordered_list) - set_ordered_list_type_to_element (list, format); - - if (level >= 0 && !html_mode) { - gint offset; - - offset = (level + 1) * SPACES_PER_LIST_LEVEL; - - offset += !inserting_unordered_list ? - SPACES_ORDERED_LIST_FIRST_LEVEL - SPACES_PER_LIST_LEVEL: 0; - - e_html_editor_selection_set_paragraph_style ( - selection, list, -1, -offset, ""); - - if (inserting_unordered_list) - webkit_dom_element_set_attribute (list, "data-evo-plain-text", "", NULL); - } - - return list; -} - -static WebKitDOMNode * -get_list_item_node_from_child (WebKitDOMNode *child) -{ - WebKitDOMNode *parent = webkit_dom_node_get_parent_node (child); - - while (parent && !WEBKIT_DOM_IS_HTMLLI_ELEMENT (parent)) - parent = webkit_dom_node_get_parent_node (parent); - - return parent; -} - -static WebKitDOMNode * -get_list_node_from_child (WebKitDOMNode *child) -{ - WebKitDOMNode *parent = get_list_item_node_from_child (child); - - return webkit_dom_node_get_parent_node (parent); -} - -static WebKitDOMElement * -do_format_change_list_to_list (WebKitDOMElement *list_to_process, - WebKitDOMElement *new_list_template, - EHTMLEditorSelectionBlockFormat to) -{ - EHTMLEditorSelectionBlockFormat current_format; - - current_format = get_list_format_from_node ( - WEBKIT_DOM_NODE (list_to_process)); - if (to == current_format) { - /* Same format, skip it. */ - return list_to_process; - } else if (current_format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST && - to >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST) { - /* Changing from ordered list type to another ordered list type. */ - set_ordered_list_type_to_element (list_to_process, to); - return list_to_process; - } else { - WebKitDOMNode *clone, *child; - - /* Create new list from template. */ - clone = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (new_list_template), FALSE); - - /* Insert it before the list that we are processing. */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (list_to_process)), - clone, - WEBKIT_DOM_NODE (list_to_process), - NULL); - - /* Move all it children to the new one. */ - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (list_to_process)))) - webkit_dom_node_append_child (clone, child, NULL); - - remove_node (WEBKIT_DOM_NODE (list_to_process)); - - return WEBKIT_DOM_ELEMENT (clone); - } - - return NULL; -} - -static void -format_change_list_from_list (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - EHTMLEditorSelectionBlockFormat to, - gboolean html_mode) -{ - gboolean after_selection_end = FALSE; - WebKitDOMElement *selection_start_marker, *selection_end_marker, *new_list; - WebKitDOMNode *source_list, *source_list_clone, *current_list, *item; - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - if (!selection_start_marker || !selection_end_marker) - return; - - /* Copy elements from previous block to list */ - item = get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start_marker)); - source_list = webkit_dom_node_get_parent_node (item); - current_list = source_list; - source_list_clone = webkit_dom_node_clone_node (source_list, FALSE); - - new_list = create_list_element (selection, document, to, 0, html_mode); - - if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented")) - element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented"); - - while (item) { - gboolean selection_end; - WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item); - - selection_end = webkit_dom_node_contains ( - item, WEBKIT_DOM_NODE (selection_end_marker)); - - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) { - /* Actual node is an item, just copy it. */ - webkit_dom_node_append_child ( - after_selection_end ? - source_list_clone : WEBKIT_DOM_NODE (new_list), - item, - NULL); - } else if (node_is_list (item) && !selection_end && !after_selection_end) { - /* Node is a list and it doesn't contain the selection end - * marker, we can process the whole list. */ - gint ii; - WebKitDOMNodeList *list; - WebKitDOMElement *processed_list; - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL); - ii = webkit_dom_node_list_get_length (list); - g_object_unref (list); - - /* Process every sublist separately. */ - while (ii) { - WebKitDOMElement *list_to_process; - - list_to_process = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL); - if (list_to_process) - do_format_change_list_to_list (list_to_process, new_list, to); - ii--; - } - - /* Process the current list. */ - processed_list = do_format_change_list_to_list ( - WEBKIT_DOM_ELEMENT (item), new_list, to); - - webkit_dom_node_append_child ( - after_selection_end ? - source_list_clone : WEBKIT_DOM_NODE (new_list), - WEBKIT_DOM_NODE (processed_list), - NULL); - } else if (node_is_list (item) && !after_selection_end) { - /* Node is a list and it contains the selection end marker, - * thus we have to process it until we find the marker. */ - gint ii; - WebKitDOMNodeList *list; - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL); - ii = webkit_dom_node_list_get_length (list); - g_object_unref (list); - - /* No nested lists - process the items. */ - if (ii == 0) { - WebKitDOMNode *clone, *child; - - clone = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (new_list), FALSE); - - webkit_dom_node_append_child ( - after_selection_end ? - source_list_clone : WEBKIT_DOM_NODE (new_list), - clone, - NULL); - - while ((child = webkit_dom_node_get_first_child (item))) { - webkit_dom_node_append_child (clone, child, NULL); - if (webkit_dom_node_contains (child, WEBKIT_DOM_NODE (selection_end_marker))) - break; - } - - if (webkit_dom_node_get_first_child (item)) - webkit_dom_node_append_child ( - after_selection_end ? - source_list_clone : WEBKIT_DOM_NODE (new_list), - item, - NULL); - else - remove_node (item); - } else { - gboolean done = FALSE; - WebKitDOMNode *tmp_parent = WEBKIT_DOM_NODE (new_list); - WebKitDOMNode *tmp_item = WEBKIT_DOM_NODE (item); - - while (!done) { - WebKitDOMNode *clone, *child; - - clone = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (new_list), FALSE); - - webkit_dom_node_append_child ( - tmp_parent, clone, NULL); - - while ((child = webkit_dom_node_get_first_child (tmp_item))) { - if (!webkit_dom_node_contains (child, WEBKIT_DOM_NODE (selection_end_marker))) { - webkit_dom_node_append_child (clone, child, NULL); - } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (child)) { - webkit_dom_node_append_child (clone, child, NULL); - done = TRUE; - break; - } else { - tmp_parent = clone; - tmp_item = child; - break; - } - } - } - } - } else { - webkit_dom_node_append_child ( - after_selection_end ? - source_list_clone : WEBKIT_DOM_NODE (new_list), - item, - NULL); - } - - if (selection_end) { - source_list_clone = webkit_dom_node_clone_node (current_list, FALSE); - after_selection_end = TRUE; - } - - if (!next_item) { - if (after_selection_end) - break; - - current_list = webkit_dom_node_get_next_sibling (current_list); - if (!node_is_list_or_item (current_list)) - break; - if (node_is_list (current_list)) { - next_item = webkit_dom_node_get_first_child (current_list); - if (!node_is_list_or_item (next_item)) - break; - } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (current_list)) { - next_item = current_list; - current_list = webkit_dom_node_get_parent_node (next_item); - } - } - - item = next_item; - } - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (source_list), - WEBKIT_DOM_NODE (source_list_clone), - webkit_dom_node_get_next_sibling (source_list), - NULL); - - if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (new_list))) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (source_list_clone), - WEBKIT_DOM_NODE (new_list), - source_list_clone, - NULL); - - if (!webkit_dom_node_has_child_nodes (source_list)) - remove_node (source_list); - - if (!webkit_dom_node_has_child_nodes (source_list_clone)) - remove_node (source_list_clone); - - merge_lists_if_possible (WEBKIT_DOM_NODE (new_list)); -} - -static void -set_block_alignment (WebKitDOMElement *element, - const gchar *class) -{ - WebKitDOMElement *parent; - - element_remove_class (element, "-x-evo-align-center"); - element_remove_class (element, "-x-evo-align-right"); - element_add_class (element, class); - parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element)); - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - element_remove_class (parent, "-x-evo-align-center"); - element_remove_class (parent, "-x-evo-align-right"); - parent = webkit_dom_node_get_parent_element ( - WEBKIT_DOM_NODE (parent)); - } -} - -void -e_html_editor_selection_get_selection_coordinates (EHTMLEditorSelection *selection, - guint *start_x, - guint *start_y, - guint *end_x, - guint *end_y) -{ - EHTMLEditorView *view; - gboolean created_selection_markers = FALSE; - guint local_x = 0, local_y = 0; - WebKitDOMElement *element, *parent; - WebKitDOMDocument *document; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (start_x != NULL); - g_return_if_fail (start_y != NULL); - g_return_if_fail (end_x != NULL); - g_return_if_fail (end_y != NULL); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - g_object_unref (view); - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (!element) { - created_selection_markers = TRUE; - e_html_editor_selection_save (selection); - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (!element) - return; - } - - parent = element; - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - local_x += (guint) webkit_dom_element_get_offset_left (parent); - local_y += (guint) webkit_dom_element_get_offset_top (parent); - parent = webkit_dom_element_get_offset_parent (parent); - } - - if (start_x) - *start_x = local_x; - if (start_y) - *start_y = local_y; - - if (e_html_editor_selection_is_collapsed (selection)) { - *end_x = local_x; - *end_y = local_y; - - if (created_selection_markers) - e_html_editor_selection_restore (selection); - - goto workaroud; - } - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - local_x = 0; - local_y = 0; - - parent = element; - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - local_x += (guint) webkit_dom_element_get_offset_left (parent); - local_y += (guint) webkit_dom_element_get_offset_top (parent); - parent = webkit_dom_element_get_offset_parent (parent); - } - - if (end_x) - *end_x = local_x; - if (end_y) - *end_y = local_y; - - if (created_selection_markers) - e_html_editor_selection_restore (selection); - - workaroud: - /* Workaround for bug 749712 on the Evolution side. The cause of the bug - * is that WebKit is having problems determining the right line height - * for some fonts and font sizes (the right and wrong value differ by 1). - * To fix this we will add an extra one to the final top offset. This is - * safe to do even for fonts and font sizes that don't behave badly as we - * will still get the right element as we use fonts bigger than 1 pixel. */ - *start_y += 1; - *end_y += 1; -} - -/** - * e_html_editor_selection_set_alignment: - * @selection: an #EHTMLEditorSelection - * @alignment: an #EHTMLEditorSelectionAlignment value to apply - * - * Sets alignment of current paragraph to give @alignment. - */ -void -e_html_editor_selection_set_alignment (EHTMLEditorSelection *selection, - EHTMLEditorSelectionAlignment alignment) -{ - EHTMLEditorSelectionAlignment current_alignment; - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - gboolean after_selection_end = FALSE; - const gchar *class = ""; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *block; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - current_alignment = e_html_editor_selection_get_alignment (selection); - - if (current_alignment == alignment) - return; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - switch (alignment) { - case E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER: - class = "-x-evo-align-center"; - break; - - case E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT: - break; - - case E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT: - class = "-x-evo-align-right"; - break; - } - - selection->priv->alignment = alignment; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - if (!selection_start_marker) { - g_object_unref (view); - return; - } - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_ALIGNMENT; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - ev->data.style.from = current_alignment; - ev->data.style.to = alignment; - } - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - while (block && !after_selection_end) { - WebKitDOMNode *next_block; - - next_block = webkit_dom_node_get_next_sibling (block); - - after_selection_end = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_end_marker)); - - if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-indented")) { - gint ii, length; - WebKitDOMNodeList *list; - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (block), - ".-x-evo-indented > *:not(.-x-evo-indented):not(li)", - NULL); - length = webkit_dom_node_list_get_length (list); - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *item = webkit_dom_node_list_item (list, ii); - - set_block_alignment (WEBKIT_DOM_ELEMENT (item), class); - - after_selection_end = webkit_dom_node_contains ( - item, WEBKIT_DOM_NODE (selection_end_marker)); - g_object_unref (item); - if (after_selection_end) - break; - } - - g_object_unref (list); - } else { - set_block_alignment (WEBKIT_DOM_ELEMENT (block), class); - } - - block = next_block; - } - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "alignment"); -} - -/** - * e_html_editor_selection_get_background_color: - * @selection: an #EHTMLEditorSelection - * - * Returns background color of currently selected text or letter at current - * cursor position. - * - * Returns: A string with code of current background color. - */ -const gchar * -e_html_editor_selection_get_background_color (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - WebKitDOMNode *ancestor; - WebKitDOMRange *range; - WebKitDOMCSSStyleDeclaration *css; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_val_if_fail (view != NULL, FALSE); - - if (!e_html_editor_view_get_html_mode (view)) { - g_object_unref (view); - return "#ffffff"; - } - - g_object_unref (view); - - range = html_editor_selection_get_current_range (selection); - - ancestor = webkit_dom_range_get_common_ancestor_container (range, NULL); - - css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (ancestor)); - g_free (selection->priv->background_color); - selection->priv->background_color = - webkit_dom_css_style_declaration_get_property_value ( - css, "background-color"); - - g_object_unref (css); - g_object_unref (range); - - return selection->priv->background_color; -} - -/** - * e_html_editor_selection_set_background_color: - * @selection: an #EHTMLEditorSelection - * @color: code of new background color to set - * - * Changes background color of current selection or letter at current cursor - * position to @color. - */ -void -e_html_editor_selection_set_background_color (EHTMLEditorSelection *selection, - const gchar *color) -{ - EHTMLEditorView *view; - EHTMLEditorViewCommand command; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (color != NULL && *color != '\0'); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - command = E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR; - e_html_editor_view_exec_command (view, command, color); - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "background-color"); -} - -static gint -get_indentation_level (WebKitDOMElement *element) -{ - WebKitDOMElement *parent; - gint level = 0; - - if (element_has_class (element, "-x-evo-indented")) - level++; - - parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element)); - /* Count level of indentation */ - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - if (element_has_class (parent, "-x-evo-indented")) - level++; - - parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent)); - } - - return level; -} - -static WebKitDOMNode * -get_block_node (WebKitDOMRange *range) -{ - WebKitDOMNode *node; - - node = webkit_dom_range_get_common_ancestor_container (range, NULL); - node = e_html_editor_get_parent_block_node_from_child (node); - - return node; -} - -/** - * e_html_editor_selection_get_block_format: - * @selection: an #EHTMLEditorSelection - * - * Returns block format of current paragraph. - * - * Returns: #EHTMLEditorSelectionBlockFormat - */ -EHTMLEditorSelectionBlockFormat -e_html_editor_selection_get_block_format (EHTMLEditorSelection *selection) -{ - WebKitDOMNode *node; - WebKitDOMRange *range; - WebKitDOMElement *element; - EHTMLEditorSelectionBlockFormat result; - - g_return_val_if_fail ( - E_IS_HTML_EDITOR_SELECTION (selection), - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH); - - range = html_editor_selection_get_current_range (selection); - if (!range) - return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH; - - node = webkit_dom_range_get_start_container (range, NULL); - - if ((element = e_html_editor_dom_node_find_parent_element (node, "UL"))) { - WebKitDOMElement *tmp_element; - - tmp_element = e_html_editor_dom_node_find_parent_element (node, "OL"); - if (tmp_element) { - if (webkit_dom_node_contains (WEBKIT_DOM_NODE (tmp_element), WEBKIT_DOM_NODE (element))) - result = get_list_format_from_node (WEBKIT_DOM_NODE (element)); - else - result = get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element)); - } else - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST; - } else if ((element = e_html_editor_dom_node_find_parent_element (node, "OL")) != NULL) { - WebKitDOMElement *tmp_element; - - tmp_element = e_html_editor_dom_node_find_parent_element (node, "UL"); - if (tmp_element) { - if (webkit_dom_node_contains (WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (tmp_element))) - result = get_list_format_from_node (WEBKIT_DOM_NODE (element)); - else - result = get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element)); - } else - result = get_list_format_from_node (WEBKIT_DOM_NODE (element)); - } else if (e_html_editor_dom_node_find_parent_element (node, "PRE")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE; - } else if (e_html_editor_dom_node_find_parent_element (node, "ADDRESS")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS; - } else if (e_html_editor_dom_node_find_parent_element (node, "H1")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1; - } else if (e_html_editor_dom_node_find_parent_element (node, "H2")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2; - } else if (e_html_editor_dom_node_find_parent_element (node, "H3")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3; - } else if (e_html_editor_dom_node_find_parent_element (node, "H4")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4; - } else if (e_html_editor_dom_node_find_parent_element (node, "H5")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5; - } else if (e_html_editor_dom_node_find_parent_element (node, "H6")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6; - } else if ((element = e_html_editor_dom_node_find_parent_element (node, "BLOCKQUOTE")) != NULL) { - if (element_has_class (element, "-x-evo-indented")) - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH; - else { - WebKitDOMNode *block = get_block_node (range); - - if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (block) || - element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-paragraph")) - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH; - else { - /* Paragraphs inside quote */ - if ((element = e_html_editor_dom_node_find_parent_element (node, "DIV")) != NULL) - if (element_has_class (element, "-x-evo-paragraph")) - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH; - else - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE; - else - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE; - } - } - } else if (e_html_editor_dom_node_find_parent_element (node, "P")) { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH; - } else { - result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH; - } - - g_object_unref (range); - - return result; -} - -void -remove_wrapping_from_element (WebKitDOMElement *element) -{ - WebKitDOMNodeList *list; - gint ii, length; - - g_return_if_fail (element != NULL); - - list = webkit_dom_element_query_selector_all ( - element, "br.-x-evo-wrap-br", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - WebKitDOMNode *parent; - - parent = e_html_editor_get_parent_block_node_from_child (node); - if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-user-wrapped")) - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - list = webkit_dom_element_query_selector_all ( - element, "span[data-hidden-space]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *hidden_space_node; - WebKitDOMNode *parent; - - hidden_space_node = webkit_dom_node_list_item (list, ii); - parent = e_html_editor_get_parent_block_node_from_child (hidden_space_node); - if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-user-wrapped")) { - webkit_dom_html_element_set_outer_text ( - WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL); - } - g_object_unref (hidden_space_node); - } - g_object_unref (list); - - webkit_dom_node_normalize (WEBKIT_DOM_NODE (element)); -} - -void -remove_quoting_from_element (WebKitDOMElement *element) -{ - gint ii, length; - WebKitDOMNodeList *list; - - g_return_if_fail (element != NULL); - - list = webkit_dom_element_query_selector_all ( - element, "span.-x-evo-quoted", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - list = webkit_dom_element_query_selector_all ( - element, "br.-x-evo-temp-br", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - webkit_dom_node_normalize (WEBKIT_DOM_NODE (element)); -} - -static gint -get_citation_level (WebKitDOMNode *node) -{ - WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); - gint level = 0; - - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && - webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type")) - level++; - - parent = webkit_dom_node_get_parent_node (parent); - } - - return level; -} - -static gboolean -is_citation_node (WebKitDOMNode *node) -{ - gchar *value; - - if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) - return FALSE; - - value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type"); - - /* citation == <blockquote type='cite'> */ - if (g_strcmp0 (value, "cite") == 0) { - g_free (value); - return TRUE; - } else { - g_free (value); - return FALSE; - } -} - -static WebKitDOMNode * -indent_block (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - WebKitDOMNode *block, - gint width) -{ - WebKitDOMElement *element; - WebKitDOMNode *sibling, *tmp; - - sibling = webkit_dom_node_get_previous_sibling (block); - if (WEBKIT_DOM_IS_ELEMENT (sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-indented")) { - element = WEBKIT_DOM_ELEMENT (sibling); - } else { - element = e_html_editor_selection_get_indented_element ( - selection, document, width); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - WEBKIT_DOM_NODE (element), - block, - NULL); - } - - /* Remove style and let the paragraph inherit it from parent */ - if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-paragraph")) - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (block), "style"); - - tmp = webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), - block, - NULL); - - sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - - while (WEBKIT_DOM_IS_ELEMENT (sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-indented")) { - WebKitDOMNode *next_sibling; - WebKitDOMNode *child; - - next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (sibling)); - - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (sibling)))) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), - child, - NULL); - } - remove_node (sibling); - sibling = next_sibling; - } - - return tmp; -} - -static void -remove_node_and_parents_if_empty (WebKitDOMNode *node) -{ - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (node)); - - remove_node (WEBKIT_DOM_NODE (node)); - - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - WebKitDOMNode *prev_sibling, *next_sibling; - - prev_sibling = webkit_dom_node_get_previous_sibling (parent); - next_sibling = webkit_dom_node_get_next_sibling (parent); - /* Empty or BR as sibling, but no sibling after it. */ - if ((!prev_sibling || - (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling) && - !webkit_dom_node_get_previous_sibling (prev_sibling))) && - (!next_sibling || - (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling) && - !webkit_dom_node_get_next_sibling (next_sibling)))) { - WebKitDOMNode *tmp; - - tmp = webkit_dom_node_get_parent_node (parent); - remove_node (parent); - parent = tmp; - } else { - if (!webkit_dom_node_get_first_child (parent)) - remove_node (parent); - return; - } - } -} - -static gboolean -do_format_change_list_to_block (EHTMLEditorSelection *selection, - EHTMLEditorSelectionBlockFormat format, - WebKitDOMNode *item, - const gchar *value, - WebKitDOMDocument *document) -{ - gboolean after_end = FALSE; - gint level; - WebKitDOMElement *element, *selection_end; - WebKitDOMNode *node, *source_list; - - selection_end = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - source_list = webkit_dom_node_get_parent_node (item); - while (source_list) { - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node (source_list); - if (node_is_list (parent)) - source_list = parent; - else - break; - } - - if (webkit_dom_node_contains (source_list, WEBKIT_DOM_NODE (selection_end))) - source_list = split_node_into_two (item, -1); - else { - source_list = webkit_dom_node_get_next_sibling (source_list); - } - - /* Process all nodes that are in selection one by one */ - while (item && WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) { - WebKitDOMNode *next_item; - - next_item = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (item)); - if (!next_item) { - WebKitDOMNode *parent; - WebKitDOMNode *tmp = item; - - while (tmp) { - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp)); - if (!node_is_list (parent)) - break; - - next_item = webkit_dom_node_get_next_sibling (parent); - if (node_is_list (next_item)) { - next_item = webkit_dom_node_get_first_child (next_item); - break; - } else if (next_item && !WEBKIT_DOM_IS_HTMLLI_ELEMENT (next_item)) { - next_item = webkit_dom_node_get_next_sibling (next_item); - break; - } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (next_item)) { - break; - } - tmp = parent; - } - } else if (node_is_list (next_item)) { - next_item = webkit_dom_node_get_first_child (next_item); - } else if (!WEBKIT_DOM_IS_HTMLLI_ELEMENT (next_item)) { - next_item = webkit_dom_node_get_next_sibling (item); - continue; - } - - if (!after_end) { - after_end = webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end)); - - level = get_indentation_level (WEBKIT_DOM_ELEMENT (item)); - - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH) { - element = e_html_editor_selection_get_paragraph_element ( - selection, document, -1, 0); - } else - element = webkit_dom_document_create_element ( - document, value, NULL); - - while ((node = webkit_dom_node_get_first_child (item))) - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), node, NULL); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (source_list), - WEBKIT_DOM_NODE (element), - source_list, - NULL); - - if (level > 0) { - gint final_width = 0; - - node = WEBKIT_DOM_NODE (element); - - if (element_has_class (element, "-x-evo-paragraph")) - final_width = selection->priv->word_wrap_length - - SPACES_PER_INDENTATION * level; - - while (level--) - node = indent_block (selection, document, node, final_width); - } - - remove_node_and_parents_if_empty (item); - } else - break; - - item = next_item; - } - - remove_node_if_empty (source_list); - - return after_end; -} - -static void -format_change_list_to_block (EHTMLEditorSelection *selection, - EHTMLEditorSelectionBlockFormat format, - const gchar *value, - WebKitDOMDocument *document) -{ - WebKitDOMElement *selection_start; - WebKitDOMNode *item; - - selection_start = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - - item = get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start)); - - do_format_change_list_to_block (selection, format, item, value, document); -} - -static void -change_leading_space_to_nbsp (WebKitDOMNode *block) -{ - WebKitDOMNode *child; - - if (!WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block)) - return; - - if ((child = webkit_dom_node_get_first_child (block)) && - WEBKIT_DOM_IS_CHARACTER_DATA (child)) { - gchar *data; - - data = webkit_dom_character_data_substring_data ( - WEBKIT_DOM_CHARACTER_DATA (child), 0, 1, NULL); - - if (data && *data == ' ') - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (child), 0, 1, UNICODE_NBSP, NULL); - g_free (data); - } -} - -static void -change_trailing_space_in_block_to_nbsp (WebKitDOMNode *block) -{ - WebKitDOMNode *child; - - if ((child = webkit_dom_node_get_last_child (block)) && - WEBKIT_DOM_IS_CHARACTER_DATA (child)) { - gchar *tmp; - gulong length; - - length = webkit_dom_character_data_get_length ( - WEBKIT_DOM_CHARACTER_DATA (child)); - - tmp = webkit_dom_character_data_substring_data ( - WEBKIT_DOM_CHARACTER_DATA (child), length - 1, 1, NULL); - if (tmp && *tmp == ' ') { - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (child), - length - 1, - 1, - UNICODE_NBSP, - NULL); - } - g_free (tmp); - } -} - -static void -change_space_before_selection_to_nbsp (WebKitDOMNode *node) -{ - WebKitDOMNode *prev_sibling; - - if ((prev_sibling = webkit_dom_node_get_previous_sibling (node))) { - if (WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling)) { - gchar *tmp; - gulong length; - - length = webkit_dom_character_data_get_length ( - WEBKIT_DOM_CHARACTER_DATA (prev_sibling)); - - tmp = webkit_dom_character_data_substring_data ( - WEBKIT_DOM_CHARACTER_DATA (prev_sibling), length - 1, 1, NULL); - if (tmp && *tmp == ' ') { - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (prev_sibling), - length - 1, - 1, - UNICODE_NBSP, - NULL); - } - g_free (tmp); - } - } -} - -static gboolean -process_block_to_block (EHTMLEditorSelection *selection, - EHTMLEditorView *view, - WebKitDOMDocument *document, - EHTMLEditorSelectionBlockFormat format, - const gchar *value, - WebKitDOMNode *block, - WebKitDOMNode *end_block, - WebKitDOMNode *blockquote, - gboolean html_mode) -{ - gboolean after_selection_end = FALSE; - WebKitDOMNode *next_block; - - while (!after_selection_end && block) { - gboolean quoted = FALSE; - gboolean empty = FALSE; - gchar *content; - gint citation_level = 0; - WebKitDOMNode *child; - WebKitDOMElement *element; - - if (is_citation_node (block)) { - gboolean finished; - - next_block = webkit_dom_node_get_next_sibling (block); - finished = process_block_to_block ( - selection, - view, - document, - format, - value, - webkit_dom_node_get_first_child (block), - end_block, - blockquote, - html_mode); - - if (finished) - return TRUE; - - block = next_block; - - continue; - } - - if (webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) { - quoted = TRUE; - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block)); - } - - if (!html_mode) - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block)); - - after_selection_end = webkit_dom_node_is_same_node (block, end_block); - - next_block = webkit_dom_node_get_next_sibling (block); - - if (node_is_list (block)) { - WebKitDOMNode *item; - - item = webkit_dom_node_get_first_child (block); - while (item && !WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) - item = webkit_dom_node_get_first_child (item); - - if (item && do_format_change_list_to_block (selection, format, item, value, document)) - return TRUE; - - block = next_block; - - continue; - } - - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH || - format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) - element = e_html_editor_selection_get_paragraph_element ( - selection, document, -1, 0); - else - element = webkit_dom_document_create_element ( - document, value, NULL); - - content = webkit_dom_node_get_text_content (block); - - empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0); - g_free (content); - - change_leading_space_to_nbsp (block); - change_trailing_space_in_block_to_nbsp (block); - - while ((child = webkit_dom_node_get_first_child (block))) { - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) - empty = FALSE; - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), child, NULL); - } - - if (empty) { - WebKitDOMElement *br; - - br = webkit_dom_document_create_element ( - document, "BR", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (br), NULL); - } - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - WEBKIT_DOM_NODE (element), - block, - NULL); - - remove_node (block); - - if (!next_block && !after_selection_end) { - gint citation_level; - - citation_level = get_citation_level (WEBKIT_DOM_NODE (element)); - - if (citation_level > 0) { - next_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - next_block = webkit_dom_node_get_next_sibling (next_block); - } - } - - block = next_block; - - if (!html_mode && - (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH || - format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE)) { - gint quote; - - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) - citation_level = 1; - else - citation_level = get_citation_level (WEBKIT_DOM_NODE (element)); - quote = citation_level ? citation_level * 2 : 0; - - if (citation_level > 0) - element = e_html_editor_selection_wrap_paragraph_length ( - selection, element, selection->priv->word_wrap_length - quote); - } - - if (blockquote && format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) { - webkit_dom_node_append_child ( - blockquote, WEBKIT_DOM_NODE (element), NULL); - if (!html_mode) - e_html_editor_view_quote_plain_text_element_after_wrapping (document, element, 1); - } else if (!html_mode && quoted) { - if (citation_level > 0) - e_html_editor_view_quote_plain_text_element_after_wrapping (document, element, citation_level); - else - e_html_editor_view_quote_plain_text_element (view, element); - } - } - - return after_selection_end; -} - -static void -format_change_block_to_block (EHTMLEditorSelection *selection, - EHTMLEditorSelectionBlockFormat format, - EHTMLEditorView *view, - const gchar *value, - WebKitDOMDocument *document) -{ - gboolean html_mode; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *block, *end_block, *blockquote = NULL; - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - /* If the selection was not saved, move it into the first child of body */ - if (!selection_start_marker || !selection_end_marker) { - WebKitDOMHTMLElement *body; - WebKitDOMNode *child; - - body = webkit_dom_document_get_body (document); - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - add_selection_markers_into_element_start ( - document, - WEBKIT_DOM_ELEMENT (child), - &selection_start_marker, - &selection_end_marker); - } - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - html_mode = e_html_editor_view_get_html_mode (view); - - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) { - blockquote = WEBKIT_DOM_NODE ( - webkit_dom_document_create_element (document, "BLOCKQUOTE", NULL)); - - webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (blockquote), "type", "cite", NULL); - if (!html_mode) - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (blockquote), "class", "-x-evo-plaintext-quoted", NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - blockquote, - block, - NULL); - } - - end_block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_end_marker)); - - /* Process all blocks that are in the selection one by one */ - process_block_to_block ( - selection, view, document, format, value, block, end_block, blockquote, html_mode); -} - -static void -format_change_block_to_list (EHTMLEditorSelection *selection, - EHTMLEditorSelectionBlockFormat format, - EHTMLEditorView *view, - WebKitDOMDocument *document) -{ - gboolean after_selection_end = FALSE, in_quote = FALSE; - gboolean html_mode = e_html_editor_view_get_html_mode (view); - WebKitDOMElement *selection_start_marker, *selection_end_marker, *item, *list; - WebKitDOMNode *block, *next_block; - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - /* If the selection was not saved, move it into the first child of body */ - if (!selection_start_marker || !selection_end_marker) { - WebKitDOMHTMLElement *body; - WebKitDOMNode *child; - - body = webkit_dom_document_get_body (document); - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - add_selection_markers_into_element_start ( - document, - WEBKIT_DOM_ELEMENT (child), - &selection_start_marker, - &selection_end_marker); - } - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - list = create_list_element (selection, document, format, 0, html_mode); - - if (webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) { - WebKitDOMElement *element; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - in_quote = TRUE; - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - range = webkit_dom_document_create_range (document); - - webkit_dom_range_select_node (range, block, NULL); - webkit_dom_range_collapse (range, TRUE, NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - - g_object_unref (range); - g_object_unref (dom_selection); - g_object_unref (dom_window); - - e_html_editor_view_remove_input_event_listener_from_body (view); - e_html_editor_selection_block_selection_changed (selection); - - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL); - - e_html_editor_view_register_input_event_listener_on_body (view); - e_html_editor_selection_unblock_selection_changed (selection); - - element = webkit_dom_document_query_selector ( - document, "body>br", NULL); - - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - WEBKIT_DOM_NODE (list), - WEBKIT_DOM_NODE (element), - NULL); - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - } else - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - WEBKIT_DOM_NODE (list), - block, - NULL); - - /* Process all blocks that are in the selection one by one */ - while (block && !after_selection_end) { - gboolean empty = FALSE; - gchar *content; - WebKitDOMNode *child, *parent; - - after_selection_end = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_end_marker)); - - next_block = webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (block)); - - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block)); - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block)); - - item = webkit_dom_document_create_element (document, "LI", NULL); - content = webkit_dom_node_get_text_content (block); - - empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0); - g_free (content); - - change_leading_space_to_nbsp (block); - change_trailing_space_in_block_to_nbsp (block); - - while ((child = webkit_dom_node_get_first_child (block))) { - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) - empty = FALSE; - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (item), child, NULL); - } - - /* We have to use again the hidden space to move caret into newly inserted list */ - if (empty) { - WebKitDOMElement *br; - - br = webkit_dom_document_create_element ( - document, "BR", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (item), WEBKIT_DOM_NODE (br), NULL); - } - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (list), WEBKIT_DOM_NODE (item), NULL); - - parent = webkit_dom_node_get_parent_node (block); - remove_node (block); - - if (in_quote) { - /* Remove all parents if previously removed node was the - * only one with text content */ - content = webkit_dom_node_get_text_content (parent); - while (parent && content && !*content) { - WebKitDOMNode *tmp = webkit_dom_node_get_parent_node (parent); - - remove_node (parent); - parent = tmp; - - g_free (content); - content = webkit_dom_node_get_text_content (parent); - } - g_free (content); - } - - block = next_block; - } - - merge_lists_if_possible (WEBKIT_DOM_NODE (list)); -} - -static void -format_change_list_to_list (EHTMLEditorSelection *selection, - EHTMLEditorSelectionBlockFormat format, - WebKitDOMDocument *document, - gboolean html_mode) -{ - EHTMLEditorSelectionBlockFormat prev = 0, next = 0; - gboolean done = FALSE, indented = FALSE; - gboolean selection_starts_in_first_child, selection_ends_in_last_child; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *prev_list, *current_list, *next_list; - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - current_list = get_list_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - prev_list = get_list_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - next_list = get_list_node_from_child ( - WEBKIT_DOM_NODE (selection_end_marker)); - - selection_starts_in_first_child = - webkit_dom_node_contains ( - webkit_dom_node_get_first_child (current_list), - WEBKIT_DOM_NODE (selection_start_marker)); - - selection_ends_in_last_child = - webkit_dom_node_contains ( - webkit_dom_node_get_last_child (current_list), - WEBKIT_DOM_NODE (selection_end_marker)); - - indented = element_has_class (WEBKIT_DOM_ELEMENT (current_list), "-x-evo-indented"); - - if (!prev_list || !next_list || indented) { - format_change_list_from_list (selection, document, format, html_mode); - return; - } - - if (webkit_dom_node_is_same_node (prev_list, next_list)) { - prev_list = webkit_dom_node_get_previous_sibling ( - webkit_dom_node_get_parent_node ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)))); - next_list = webkit_dom_node_get_next_sibling ( - webkit_dom_node_get_parent_node ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_end_marker)))); - if (!prev_list || !next_list) { - format_change_list_from_list (selection, document, format, html_mode); - return; - } - } - - prev = get_list_format_from_node (prev_list); - next = get_list_format_from_node (next_list); - - if (format != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE) { - if (format == prev && prev != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE) { - if (selection_starts_in_first_child && selection_ends_in_last_child) { - done = TRUE; - merge_list_into_list (current_list, prev_list, FALSE); - } - } - - if (format == next && next != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE) { - if (selection_starts_in_first_child && selection_ends_in_last_child) { - done = TRUE; - merge_list_into_list (next_list, prev_list, FALSE); - } - } - } - - if (done) - return; - - format_change_list_from_list (selection, document, format, html_mode); -} - -/** - * e_html_editor_selection_set_block_format: - * @selection: an #EHTMLEditorSelection - * @format: an #EHTMLEditorSelectionBlockFormat value - * - * Changes block format of current paragraph to @format. - */ -void -e_html_editor_selection_set_block_format (EHTMLEditorSelection *selection, - EHTMLEditorSelectionBlockFormat format) -{ - EHTMLEditorView *view; - EHTMLEditorSelectionBlockFormat current_format; - EHTMLEditorSelectionAlignment current_alignment; - EHTMLEditorViewHistoryEvent *ev = NULL; - const gchar *value; - gboolean from_list = FALSE, to_list = FALSE, html_mode; - WebKitDOMDocument *document; - WebKitDOMRange *range; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - current_format = e_html_editor_selection_get_block_format (selection); - if (current_format == format) { - return; - } - - switch (format) { - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE: - value = "BLOCKQUOTE"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1: - value = "H1"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2: - value = "H2"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3: - value = "H3"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4: - value = "H4"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5: - value = "H5"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6: - value = "H6"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH: - value = "P"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE: - value = "PRE"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS: - value = "ADDRESS"; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST: - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA: - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN: - to_list = TRUE; - value = NULL; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST: - to_list = TRUE; - value = NULL; - break; - case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE: - default: - value = NULL; - break; - } - - /* H1 - H6 have bold font by default */ - if (format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1 && - format <= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6) - selection->priv->is_bold = TRUE; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - html_mode = e_html_editor_view_get_html_mode (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - from_list = - current_format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST; - - range = html_editor_selection_get_current_range (selection); - if (!range) { - g_object_unref (view); - return; - } - - current_alignment = selection->priv->alignment; - - e_html_editor_selection_save (selection); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - if (format != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) - ev->type = HISTORY_BLOCK_FORMAT; - else - ev->type = HISTORY_BLOCKQUOTE; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - if (format != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) { - ev->data.style.from = current_format; - ev->data.style.to = format; - } else { - WebKitDOMDocumentFragment *fragment; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *block, *end_block; - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - end_block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_end_marker)); - if (webkit_dom_range_get_collapsed (range, NULL) || - webkit_dom_node_is_same_node (block, end_block)) { - fragment = webkit_dom_document_create_document_fragment (document); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (block, TRUE), - NULL); - } else { - fragment = webkit_dom_range_clone_contents (range, NULL); - webkit_dom_node_replace_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (block, TRUE), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), - NULL); - - webkit_dom_node_replace_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (end_block, TRUE), - webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)), - NULL); - } - ev->data.fragment = fragment; - } - } - - g_object_unref (range); - - if (current_format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE) { - WebKitDOMElement *selection_marker; - - selection_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (selection_marker) - change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker)); - selection_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - if (selection_marker) - change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker)); - } - - if (from_list && to_list) - format_change_list_to_list (selection, format, document, html_mode); - - if (!from_list && !to_list) - format_change_block_to_block (selection, format, view, value, document); - - if (from_list && !to_list) { - format_change_list_to_block (selection, format, value, document); - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE) { - e_html_editor_selection_restore (selection); - format_change_block_to_block (selection, format, view, value, document); - } - } - - if (!from_list && to_list) - format_change_block_to_list (selection, format, view, document); - - e_html_editor_selection_restore (selection); - - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - /* When changing the format we need to re-set the alignment */ - e_html_editor_selection_set_alignment (selection, current_alignment); - - e_html_editor_view_set_changed (view, TRUE); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "block-format"); -} - -/** - * e_html_editor_selection_get_font_color: - * @selection: an #EHTMLEditorSelection - * @rgba: a #GdkRGBA object to be set to current font color - * - * Sets @rgba to contain color of current text selection or letter at current - * cursor position. - */ -void -e_html_editor_selection_get_font_color (EHTMLEditorSelection *selection, - GdkRGBA *rgba) -{ - EHTMLEditorView *view; - gchar *color; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - if (!e_html_editor_view_get_html_mode (view)) { - g_object_unref (view); - *rgba = black; - return; - } - - color = get_font_property (selection, "color"); - if (!(color && *color)) { - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - g_free (color); - color = webkit_dom_html_body_element_get_text (WEBKIT_DOM_HTML_BODY_ELEMENT (body)); - if (!(color && *color)) { - *rgba = black; - g_object_unref (view); - g_free (color); - return; - } - } - - gdk_rgba_parse (rgba, color); - g_free (color); - g_object_unref (view); -} - -/** - * e_html_editor_selection_set_font_color: - * @selection: an #EHTMLEditorSelection - * @rgba: a #GdkRGBA - * - * Sets font color of current selection or letter at current cursor position to - * color defined in @rgba. - */ -void -e_html_editor_selection_set_font_color (EHTMLEditorSelection *selection, - const GdkRGBA *rgba) -{ - EHTMLEditorView *view; - EHTMLEditorViewCommand command; - EHTMLEditorViewHistoryEvent *ev = NULL; - guint32 rgba_value; - gchar *color; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (!rgba) - rgba = &black; - - rgba_value = e_rgba_to_value ((GdkRGBA *) rgba); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - command = E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR; - color = g_strdup_printf ("#%06x", rgba_value); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_FONT_COLOR; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - ev->data.string.from = g_strdup (selection->priv->font_color); - ev->data.string.to = g_strdup (color); - } - - g_free (selection->priv->font_color); - selection->priv->font_color = g_strdup (color); - e_html_editor_view_exec_command (view, command, color); - g_free (color); - - if (ev) { - ev->after.start.x = ev->before.start.x; - ev->after.start.y = ev->before.start.y; - ev->after.end.x = ev->before.end.x; - ev->after.end.y = ev->before.end.y; - - e_html_editor_view_insert_new_history_event (view, ev); - } - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "font-color"); -} - -/** - * e_html_editor_selection_get_font_name: - * @selection: an #EHTMLEditorSelection - * - * Returns name of font used in current selection or at letter at current cursor - * position. - * - * Returns: A string with font name. [transfer-none] - */ -const gchar * -e_html_editor_selection_get_font_name (EHTMLEditorSelection *selection) -{ - WebKitDOMNode *node; - WebKitDOMRange *range; - WebKitDOMCSSStyleDeclaration *css; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL); - - range = html_editor_selection_get_current_range (selection); - node = webkit_dom_range_get_common_ancestor_container (range, NULL); - g_object_unref (range); - - g_free (selection->priv->font_family); - css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node)); - selection->priv->font_family = - webkit_dom_css_style_declaration_get_property_value (css, "fontFamily"); - - g_object_unref (css); - - return selection->priv->font_family; -} - -/** - * e_html_editor_selection_set_font_name: - * @selection: an #EHTMLEditorSelection - * @font_name: a font name to apply - * - * Sets font name of current selection or of letter at current cursor position - * to @font_name. - */ -void -e_html_editor_selection_set_font_name (EHTMLEditorSelection *selection, - const gchar *font_name) -{ - EHTMLEditorView *view; - EHTMLEditorViewCommand command; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - command = E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME; - e_html_editor_view_exec_command (view, command, font_name); - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "font-name"); -} - -/** - * e_editor_Selection_get_font_size: - * @selection: an #EHTMLEditorSelection - * - * Returns point size of current selection or of letter at current cursor position. - */ - guint -e_html_editor_selection_get_font_size (EHTMLEditorSelection *selection) -{ - gchar *size; - guint size_int; - gboolean increment; - - g_return_val_if_fail ( - E_IS_HTML_EDITOR_SELECTION (selection), - E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL); - - size = get_font_property (selection, "size"); - if (!(size && *size)) { - g_free (size); - return E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL; - } - - /* We don't support increments, but when going through a content that - * was not written in Evolution we can find it. In this case just report - * the normal size. */ - /* FIXME: go through all parent and get the right value. */ - increment = size[0] == '+' || size[0] == '-'; - size_int = atoi (size); - g_free (size); - - if (increment || size_int == 0) - return E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL; - - return size_int; -} - -static WebKitDOMElement * -set_font_style (WebKitDOMDocument *document, - const gchar *element_name, - gboolean value) -{ - WebKitDOMElement *element; - WebKitDOMNode *parent; - - element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker"); - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - if (value) { - WebKitDOMNode *node; - WebKitDOMElement *el; - gchar *name; - - el = webkit_dom_document_create_element (document, element_name, NULL); - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (el), UNICODE_ZERO_WIDTH_SPACE, NULL); - - node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (el), node, NULL); - name = webkit_dom_node_get_local_name (parent); - if (g_strcmp0 (name, element_name) == 0 && g_strcmp0 (name, "font") != 0) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - WEBKIT_DOM_NODE (el), - webkit_dom_node_get_next_sibling (parent), - NULL); - else - webkit_dom_node_insert_before ( - parent, - WEBKIT_DOM_NODE (el), - WEBKIT_DOM_NODE (element), - NULL); - g_free (name); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (el), WEBKIT_DOM_NODE (element), NULL); - - return el; - } else { - gboolean no_sibling; - WebKitDOMNode *node, *sibling; - - node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - - /* Turning the formatting in the middle of element. */ - sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - no_sibling = sibling && - !WEBKIT_DOM_IS_HTMLBR_ELEMENT (sibling) && - !webkit_dom_node_get_next_sibling (sibling); - - if (no_sibling) { - WebKitDOMNode *clone; - WebKitDOMNode *sibling; - - clone = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (parent), FALSE); - - while ((sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)))) - webkit_dom_node_insert_before ( - clone, - sibling, - webkit_dom_node_get_first_child (clone), - NULL); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - clone, - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)), - NULL); - } - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - WEBKIT_DOM_NODE (element), - webkit_dom_node_get_next_sibling (parent), - NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - node, - webkit_dom_node_get_next_sibling (parent), - NULL); - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (sibling) && !no_sibling) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - node, - webkit_dom_node_get_next_sibling (parent), - NULL); - } - - webkit_dom_html_element_insert_adjacent_text ( - WEBKIT_DOM_HTML_ELEMENT (parent), - "afterend", - UNICODE_ZERO_WIDTH_SPACE, - NULL); - - remove_node_if_empty (parent); - } - - return NULL; -} - -/** - * e_html_editor_selection_set_font_size: - * @selection: an #EHTMLEditorSelection - * @font_size: point size to apply - * - * Sets font size of current selection or of letter at current cursor position - * to @font_size. - */ -void -e_html_editor_selection_set_font_size (EHTMLEditorSelection *selection, - guint font_size) -{ - EHTMLEditorView *view; - EHTMLEditorViewCommand command; - EHTMLEditorViewHistoryEvent *ev = NULL; - gchar *size_str; - guint current_font_size; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - current_font_size = e_html_editor_selection_get_font_size (selection); - if (current_font_size == font_size) { - g_object_unref (view); - return; - } - - e_html_editor_selection_save (selection); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_FONT_SIZE; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - ev->data.style.from = current_font_size; - ev->data.style.to = font_size; - } - - selection->priv->font_size = font_size; - size_str = g_strdup_printf ("%d", font_size); - - if (e_html_editor_selection_is_collapsed (selection)) { - WebKitDOMElement *font; - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - font = set_font_style (document, "font", font_size != 3); - if (font) - webkit_dom_element_set_attribute (font, "size", size_str, NULL); - e_html_editor_selection_restore (selection); - goto exit; - } - - e_html_editor_selection_restore (selection); - - command = E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE; - e_html_editor_view_exec_command (view, command, size_str); - - /* Text in <font size="3"></font> (size 3 is our default size) is a little - * bit smaller than font outsize it. So move it outside of it. */ - if (font_size == E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL) { - WebKitDOMDocument *document; - WebKitDOMElement *element; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - element = webkit_dom_document_query_selector (document, "font[size=\"3\"]", NULL); - if (element) { - WebKitDOMNode *child; - - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - child, - WEBKIT_DOM_NODE (element), - NULL); - - remove_node (WEBKIT_DOM_NODE (element)); - } - } - exit: - g_free (size_str); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "font-size"); -} - -/** - * e_html_editor_selection_is_citation: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current paragraph is a citation. - * - * Returns: @TRUE when current paragraph is a citation, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_citation (EHTMLEditorSelection *selection) -{ - gboolean ret_val; - gchar *value, *text_content; - WebKitDOMNode *node; - WebKitDOMRange *range; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - range = html_editor_selection_get_current_range (selection); - if (!range) - return FALSE; - - node = webkit_dom_range_get_common_ancestor_container (range, NULL); - g_object_unref (range); - - if (WEBKIT_DOM_IS_TEXT (node)) - return get_has_style (selection, "citation"); - - text_content = webkit_dom_node_get_text_content (node); - if (g_strcmp0 (text_content, "") == 0) { - g_free (text_content); - return FALSE; - } - g_free (text_content); - - value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type"); - - /* citation == <blockquote type='cite'> */ - if (strstr (value, "cite")) - ret_val = TRUE; - else - ret_val = get_has_style (selection, "citation"); - - g_free (value); - return ret_val; -} - -static WebKitDOMNode * -get_parent_indented_block (WebKitDOMNode *node) -{ - WebKitDOMNode *parent, *block = NULL; - - parent = webkit_dom_node_get_parent_node (node); - if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) - block = parent; - - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent) && - !WEBKIT_DOM_IS_HTML_HTML_ELEMENT (parent)) { - if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) - block = parent; - parent = webkit_dom_node_get_parent_node (parent); - } - - return block; -} - -static WebKitDOMElement* -get_element_for_inspection (WebKitDOMRange *range) -{ - WebKitDOMNode *node; - - node = webkit_dom_range_get_end_container (range, NULL); - /* No selection or whole body selected */ - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) - return NULL; - - return WEBKIT_DOM_ELEMENT (get_parent_indented_block (node)); -} - -/** - * e_html_editor_selection_is_indented: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current paragraph is indented. This does not include - * citations. To check, whether paragraph is a citation, use - * e_html_editor_selection_is_citation(). - * - * Returns: @TRUE when current paragraph is indented, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_indented (EHTMLEditorSelection *selection) -{ - WebKitDOMElement *element; - WebKitDOMRange *range; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - range = html_editor_selection_get_current_range (selection); - if (!range) - return FALSE; - - if (webkit_dom_range_get_collapsed (range, NULL)) { - element = get_element_for_inspection (range); - g_object_unref (range); - return element_has_class (element, "-x-evo-indented"); - } else { - WebKitDOMNode *node; - gboolean ret_val; - - node = webkit_dom_range_get_end_container (range, NULL); - /* No selection or whole body selected */ - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) - goto out; - - element = WEBKIT_DOM_ELEMENT (get_parent_indented_block (node)); - ret_val = element_has_class (element, "-x-evo-indented"); - if (!ret_val) - goto out; - - node = webkit_dom_range_get_start_container (range, NULL); - /* No selection or whole body selected */ - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) - goto out; - - element = WEBKIT_DOM_ELEMENT (get_parent_indented_block (node)); - ret_val = element_has_class (element, "-x-evo-indented"); - - g_object_unref (range); - return ret_val; - } - - out: - g_object_unref (range); - - return FALSE; -} - -static gboolean -is_in_html_mode (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view = e_html_editor_selection_ref_html_editor_view (selection); - gboolean ret_val; - - g_return_val_if_fail (view != NULL, FALSE); - - ret_val = e_html_editor_view_get_html_mode (view); - - g_object_unref (view); - - return ret_val; -} - -static gint -get_list_level (WebKitDOMNode *node) -{ - gint level = 0; - - while (node && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) { - if (node_is_list (node)) - level++; - node = webkit_dom_node_get_parent_node (node); - } - - return level; -} - -static gboolean -indent_list (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *item, *next_item; - gboolean after_selection_end = FALSE; - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - item = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) { - gboolean html_mode = is_in_html_mode (selection); - WebKitDOMElement *list; - WebKitDOMNode *source_list = webkit_dom_node_get_parent_node (item); - EHTMLEditorSelectionBlockFormat format; - - format = get_list_format_from_node (source_list); - - list = create_list_element ( - selection, document, format, get_list_level (item), html_mode); - - element_add_class (list, "-x-evo-indented"); - - webkit_dom_node_insert_before ( - source_list, WEBKIT_DOM_NODE (list), item, NULL); - - while (item && !after_selection_end) { - after_selection_end = webkit_dom_node_contains ( - item, WEBKIT_DOM_NODE (selection_end_marker)); - - next_item = webkit_dom_node_get_next_sibling (item); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (list), item, NULL); - - item = next_item; - } - - merge_lists_if_possible (WEBKIT_DOM_NODE (list)); - } - - return after_selection_end; -} - -/** - * e_html_editor_selection_indent: - * @selection: an #EHTMLEditorSelection - * - * Indents current paragraph by one level. - */ -void -e_html_editor_selection_indent (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - gboolean after_selection_start = FALSE, after_selection_end = FALSE; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *block; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - /* If the selection was not saved, move it into the first child of body */ - if (!selection_start_marker || !selection_end_marker) { - WebKitDOMHTMLElement *body; - WebKitDOMNode *child; - - body = webkit_dom_document_get_body (document); - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - add_selection_markers_into_element_start ( - document, - WEBKIT_DOM_ELEMENT (child), - &selection_start_marker, - &selection_end_marker); - } - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_INDENT; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - ev->data.style.from = 1; - ev->data.style.to = 1; - } - - block = get_parent_indented_block ( - WEBKIT_DOM_NODE (selection_start_marker)); - if (!block) - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - while (block && !after_selection_end) { - gint ii, length, level, final_width = 0; - gint word_wrap_length = selection->priv->word_wrap_length; - WebKitDOMNode *next_block; - WebKitDOMNodeList *list; - - next_block = webkit_dom_node_get_next_sibling (block); - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (block), - ".-x-evo-indented > *:not(.-x-evo-indented):not(li)", - NULL); - - after_selection_end = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_end_marker)); - - length = webkit_dom_node_list_get_length (list); - if (length == 0 && node_is_list_or_item (block)) { - after_selection_end = indent_list (selection, document); - goto next; - } - - if (length == 0) { - if (!after_selection_start) { - after_selection_start = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_start_marker)); - if (!after_selection_start) - goto next; - } - - if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-paragraph")) { - level = get_indentation_level (WEBKIT_DOM_ELEMENT (block)); - - final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1); - if (final_width < MINIMAL_PARAGRAPH_WIDTH && - !is_in_html_mode (selection)) - goto next; - } - - indent_block (selection, document, block, final_width); - - if (after_selection_end) - goto next; - } - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *block_to_process; - - block_to_process = webkit_dom_node_list_item (list, ii); - - after_selection_end = webkit_dom_node_contains ( - block_to_process, WEBKIT_DOM_NODE (selection_end_marker)); - - if (!after_selection_start) { - after_selection_start = webkit_dom_node_contains ( - block_to_process, - WEBKIT_DOM_NODE (selection_start_marker)); - if (!after_selection_start) { - g_object_unref (block_to_process); - continue; - } - } - - if (element_has_class (WEBKIT_DOM_ELEMENT (block_to_process), "-x-evo-paragraph")) { - level = get_indentation_level ( - WEBKIT_DOM_ELEMENT (block_to_process)); - - final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1); - if (final_width < MINIMAL_PARAGRAPH_WIDTH && - !is_in_html_mode (selection)) { - g_object_unref (block_to_process); - continue; - } - } - - indent_block (selection, document, block_to_process, final_width); - - if (after_selection_end) { - g_object_unref (block_to_process); - break; - } - g_object_unref (block_to_process); - } - - next: - g_object_unref (list); - - if (!after_selection_end) - block = next_block; - } - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "indented"); -} - -static const gchar * -get_css_alignment_value_class (EHTMLEditorSelectionAlignment alignment) -{ - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT) - return ""; /* Left is by default on ltr */ - - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) - return "-x-evo-align-center"; - - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) - return "-x-evo-align-right"; - - return ""; -} - -static void -unindent_list (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - gboolean after = FALSE; - WebKitDOMElement *new_list; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *source_list, *source_list_clone, *current_list, *item; - WebKitDOMNode *prev_item; - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - if (!selection_start_marker || !selection_end_marker) - return; - - /* Copy elements from previous block to list */ - item = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - source_list = webkit_dom_node_get_parent_node (item); - new_list = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_clone_node (source_list, FALSE)); - current_list = source_list; - source_list_clone = webkit_dom_node_clone_node (source_list, FALSE); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (source_list), - WEBKIT_DOM_NODE (source_list_clone), - webkit_dom_node_get_next_sibling (source_list), - NULL); - - if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented")) - element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented"); - - prev_item = source_list; - - while (item) { - WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item); - - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) { - if (after) - prev_item = webkit_dom_node_append_child ( - source_list_clone, WEBKIT_DOM_NODE (item), NULL); - else - prev_item = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (prev_item), - item, - webkit_dom_node_get_next_sibling (prev_item), - NULL); - } - - if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker))) - after = TRUE; - - if (!next_item) { - if (after) - break; - - current_list = webkit_dom_node_get_next_sibling (current_list); - next_item = webkit_dom_node_get_first_child (current_list); - } - item = next_item; - } - - remove_node_if_empty (source_list_clone); - remove_node_if_empty (source_list); -} - -static void -unindent_block (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - WebKitDOMNode *block) -{ - gboolean before_node = TRUE; - gint word_wrap_length = selection->priv->word_wrap_length; - gint level, width; - EHTMLEditorSelectionAlignment alignment; - WebKitDOMElement *element; - WebKitDOMElement *prev_blockquote = NULL, *next_blockquote = NULL; - WebKitDOMNode *block_to_process, *node_clone = NULL, *child; - - block_to_process = block; - - alignment = e_html_editor_selection_get_alignment_from_node (block_to_process); - - element = webkit_dom_node_get_parent_element (block_to_process); - - if (!WEBKIT_DOM_IS_HTML_DIV_ELEMENT (element) && - !element_has_class (element, "-x-evo-indented")) - return; - - element_add_class (WEBKIT_DOM_ELEMENT (block_to_process), "-x-evo-to-unindent"); - - level = get_indentation_level (element); - width = word_wrap_length - SPACES_PER_INDENTATION * level; - - /* Look if we have previous siblings, if so, we have to - * create new blockquote that will include them */ - if (webkit_dom_node_get_previous_sibling (block_to_process)) - prev_blockquote = e_html_editor_selection_get_indented_element ( - selection, document, width); - - /* Look if we have next siblings, if so, we have to - * create new blockquote that will include them */ - if (webkit_dom_node_get_next_sibling (block_to_process)) - next_blockquote = e_html_editor_selection_get_indented_element ( - selection, document, width); - - /* Copy nodes that are before / after the element that we want to unindent */ - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) { - if (webkit_dom_node_is_equal_node (child, block_to_process)) { - before_node = FALSE; - node_clone = webkit_dom_node_clone_node (child, TRUE); - remove_node (child); - continue; - } - - webkit_dom_node_append_child ( - before_node ? - WEBKIT_DOM_NODE (prev_blockquote) : - WEBKIT_DOM_NODE (next_blockquote), - child, - NULL); - } - - if (node_clone) { - element_remove_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-to-unindent"); - - /* Insert blockqoute with nodes that were before the element that we want to unindent */ - if (prev_blockquote) { - if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - WEBKIT_DOM_NODE (prev_blockquote), - WEBKIT_DOM_NODE (element), - NULL); - } - } - - if (level == 1 && element_has_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-paragraph")) { - e_html_editor_selection_set_paragraph_style ( - selection, WEBKIT_DOM_ELEMENT (node_clone), word_wrap_length, 0, ""); - element_add_class ( - WEBKIT_DOM_ELEMENT (node_clone), - get_css_alignment_value_class (alignment)); - } - - /* Insert the unindented element */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - node_clone, - WEBKIT_DOM_NODE (element), - NULL); - } else { - g_warn_if_reached (); - } - - /* Insert blockqoute with nodes that were after the element that we want to unindent */ - if (next_blockquote) { - if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (next_blockquote))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - WEBKIT_DOM_NODE (next_blockquote), - WEBKIT_DOM_NODE (element), - NULL); - } - } - - /* Remove old blockquote */ - remove_node (WEBKIT_DOM_NODE (element)); -} - -/** - * e_html_editor_selection_unindent: - * @selection: an #EHTMLEditorSelection - * - * Unindents current paragraph by one level. - */ -void -e_html_editor_selection_unindent (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - gboolean after_selection_start = FALSE, after_selection_end = FALSE; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *block; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - /* If the selection was not saved, move it into the first child of body */ - if (!selection_start_marker || !selection_end_marker) { - WebKitDOMHTMLElement *body; - WebKitDOMNode *child; - - body = webkit_dom_document_get_body (document); - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - add_selection_markers_into_element_start ( - document, - WEBKIT_DOM_ELEMENT (child), - &selection_start_marker, - &selection_end_marker); - } - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_INDENT; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - } - - block = get_parent_indented_block ( - WEBKIT_DOM_NODE (selection_start_marker)); - if (!block) - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - while (block && !after_selection_end) { - gint ii, length; - WebKitDOMNode *next_block; - WebKitDOMNodeList *list; - - next_block = webkit_dom_node_get_next_sibling (block); - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (block), - ".-x-evo-indented > *:not(.-x-evo-indented):not(li)", - NULL); - - after_selection_end = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_end_marker)); - - length = webkit_dom_node_list_get_length (list); - if (length == 0 && node_is_list_or_item (block)) { - unindent_list (selection, document); - goto next; - } - - if (length == 0) { - if (!after_selection_start) { - after_selection_start = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_start_marker)); - if (!after_selection_start) - goto next; - } - - unindent_block (selection, document, block); - - if (after_selection_end) - goto next; - } - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *block_to_process; - - block_to_process = webkit_dom_node_list_item (list, ii); - - after_selection_end = webkit_dom_node_contains ( - block_to_process, - WEBKIT_DOM_NODE (selection_end_marker)); - - if (!after_selection_start) { - after_selection_start = webkit_dom_node_contains ( - block_to_process, - WEBKIT_DOM_NODE (selection_start_marker)); - if (!after_selection_start) { - g_object_unref (block_to_process); - continue; - } - } - - unindent_block (selection, document, block_to_process); - - if (after_selection_end) { - g_object_unref (block_to_process); - break; - } - - g_object_unref (block_to_process); - } - next: - g_object_unref (list); - block = next_block; - } - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "indented"); -} - -typedef gboolean (*IsRightFormatNodeFunc) (WebKitDOMElement *element); - -static gboolean -is_bold_element (WebKitDOMElement *element) -{ - if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - if (element_has_tag (element, "b")) - return TRUE; - - /* Headings are bold by default */ - return WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (element); -} - -static gboolean -html_editor_selection_is_font_format (EHTMLEditorSelection *selection, - IsRightFormatNodeFunc func, - gboolean *previous_value) -{ - EHTMLEditorView *view; - gboolean ret_val = FALSE; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window = NULL; - WebKitDOMDOMSelection *dom_selection = NULL; - WebKitDOMNode *start, *end, *sibling; - WebKitDOMRange *range = NULL; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_val_if_fail (view != NULL, FALSE); - - if (!e_html_editor_view_get_html_mode (view)) - goto out; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - if (!webkit_dom_dom_selection_get_range_count (dom_selection)) - goto out; - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - if (!range) - goto out; - - if (webkit_dom_range_get_collapsed (range, NULL) && previous_value) { - WebKitDOMNode *node; - gchar* text_content; - - node = webkit_dom_range_get_common_ancestor_container (range, NULL); - /* If we are changing the format of block we have to re-set the - * format property, otherwise it will be turned off because of no - * text in block. */ - text_content = webkit_dom_node_get_text_content (node); - if (g_strcmp0 (text_content, "") == 0) { - g_free (text_content); - ret_val = *previous_value; - goto out; - } - g_free (text_content); - } - - /* Range without start or end point is a wrong range. */ - start = webkit_dom_range_get_start_container (range, NULL); - end = webkit_dom_range_get_end_container (range, NULL); - if (!start || !end) - goto out; - - if (WEBKIT_DOM_IS_TEXT (start)) - start = webkit_dom_node_get_parent_node (start); - while (start && WEBKIT_DOM_IS_ELEMENT (start) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (start)) { - /* Find the start point's parent node with given formatting. */ - if (func (WEBKIT_DOM_ELEMENT (start))) { - ret_val = TRUE; - break; - } - start = webkit_dom_node_get_parent_node (start); - } - - /* Start point doesn't have the given formatting. */ - if (!ret_val) - goto out; - - /* If the selection is collapsed, we can return early. */ - if (webkit_dom_range_get_collapsed (range, NULL)) - goto out; - - /* The selection is in the same node and that node is supposed to have - * the same formatting (otherwise it is split up with formatting element. */ - if (webkit_dom_node_is_same_node ( - webkit_dom_range_get_start_container (range, NULL), - webkit_dom_range_get_end_container (range, NULL))) - goto out; - - ret_val = FALSE; - - if (WEBKIT_DOM_IS_TEXT (end)) - end = webkit_dom_node_get_parent_node (end); - while (end && WEBKIT_DOM_IS_ELEMENT (end) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (end)) { - /* Find the end point's parent node with given formatting. */ - if (func (WEBKIT_DOM_ELEMENT (end))) { - ret_val = TRUE; - break; - } - end = webkit_dom_node_get_parent_node (end); - } - - if (!ret_val) - goto out; - - ret_val = FALSE; - - /* Now go between the end points and check the inner nodes for format validity. */ - sibling = start; - while ((sibling = webkit_dom_node_get_next_sibling (sibling))) { - if (webkit_dom_node_is_same_node (sibling, end)) { - ret_val = TRUE; - goto out; - } - - if (WEBKIT_DOM_IS_TEXT (sibling)) - goto out; - else if (func (WEBKIT_DOM_ELEMENT (sibling))) - continue; - else if (webkit_dom_node_get_first_child (sibling)) { - WebKitDOMNode *first_child; - - first_child = webkit_dom_node_get_first_child (sibling); - if (!webkit_dom_node_get_next_sibling (first_child)) - if (WEBKIT_DOM_IS_ELEMENT (first_child) && func (WEBKIT_DOM_ELEMENT (first_child))) - continue; - else - goto out; - else - goto out; - } else - goto out; - } - - sibling = end; - while ((sibling = webkit_dom_node_get_previous_sibling (sibling))) { - if (webkit_dom_node_is_same_node (sibling, start)) - break; - - if (WEBKIT_DOM_IS_TEXT (sibling)) - goto out; - else if (func (WEBKIT_DOM_ELEMENT (sibling))) - continue; - else if (webkit_dom_node_get_first_child (sibling)) { - WebKitDOMNode *first_child; - - first_child = webkit_dom_node_get_first_child (sibling); - if (!webkit_dom_node_get_next_sibling (first_child)) - if (WEBKIT_DOM_IS_ELEMENT (first_child) && func (WEBKIT_DOM_ELEMENT (first_child))) - continue; - else - goto out; - else - goto out; - } else - goto out; - } - - ret_val = TRUE; - out: - g_object_unref (view); - g_clear_object (&range); - g_clear_object (&dom_window); - g_clear_object (&dom_selection); - - return ret_val; -} - -/** - * e_html_editor_selection_is_bold: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current selection or letter at current cursor position - * is bold. - * - * Returns @TRUE when selection is bold, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_bold (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - selection->priv->is_bold = html_editor_selection_is_font_format ( - selection, (IsRightFormatNodeFunc) is_bold_element, &selection->priv->is_bold); - - return selection->priv->is_bold; -} - -static void -html_editor_selection_set_font_style (EHTMLEditorSelection *selection, - EHTMLEditorViewCommand command, - gboolean value) -{ - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - e_html_editor_selection_save (selection); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - if (command == E_HTML_EDITOR_VIEW_COMMAND_BOLD) - ev->type = HISTORY_BOLD; - else if (command == E_HTML_EDITOR_VIEW_COMMAND_ITALIC) - ev->type = HISTORY_ITALIC; - else if (command == E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE) - ev->type = HISTORY_UNDERLINE; - else if (command == E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH) - ev->type = HISTORY_STRIKETHROUGH; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - ev->data.style.from = !value; - ev->data.style.to = value; - } - - if (e_html_editor_selection_is_collapsed (selection)) { - WebKitDOMDocument *document; - const gchar *element_name = NULL; - - if (command == E_HTML_EDITOR_VIEW_COMMAND_BOLD) - element_name = "b"; - else if (command == E_HTML_EDITOR_VIEW_COMMAND_ITALIC) - element_name = "i"; - else if (command == E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE) - element_name = "u"; - else if (command == E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH) - element_name = "strike"; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - if (element_name) - set_font_style (document, element_name, value); - e_html_editor_selection_restore (selection); - - goto exit; - } - e_html_editor_selection_restore (selection); - - e_html_editor_view_exec_command (view, command, NULL); - exit: - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - g_object_unref (view); -} - -/** - * e_html_editor_selection_set_bold: - * @selection: an #EHTMLEditorSelection - * @bold: @TRUE to enable bold, @FALSE to disable - * - * Toggles bold formatting of current selection or letter at current cursor - * position, depending on whether @bold is @TRUE or @FALSE. - */ -void -e_html_editor_selection_set_bold (EHTMLEditorSelection *selection, - gboolean bold) -{ - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (e_html_editor_selection_is_bold (selection) == bold) - return; - - selection->priv->is_bold = bold; - - html_editor_selection_set_font_style ( - selection, E_HTML_EDITOR_VIEW_COMMAND_BOLD, bold); - - g_object_notify (G_OBJECT (selection), "bold"); -} - -static gboolean -is_italic_element (WebKitDOMElement *element) -{ - if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - return element_has_tag (element, "i") || element_has_tag (element, "address"); -} - -/** - * e_html_editor_selection_is_italic: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current selection or letter at current cursor position - * is italic. - * - * Returns @TRUE when selection is italic, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_italic (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - selection->priv->is_italic = html_editor_selection_is_font_format ( - selection, (IsRightFormatNodeFunc) is_italic_element, &selection->priv->is_italic); - - return selection->priv->is_italic; -} - -/** - * e_html_editor_selection_set_italic: - * @selection: an #EHTMLEditorSelection - * @italic: @TRUE to enable italic, @FALSE to disable - * - * Toggles italic formatting of current selection or letter at current cursor - * position, depending on whether @italic is @TRUE or @FALSE. - */ -void -e_html_editor_selection_set_italic (EHTMLEditorSelection *selection, - gboolean italic) -{ - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (e_html_editor_selection_is_italic (selection) == italic) - return; - - selection->priv->is_italic = italic; - - html_editor_selection_set_font_style ( - selection, E_HTML_EDITOR_VIEW_COMMAND_ITALIC, italic); - - g_object_notify (G_OBJECT (selection), "italic"); -} - -static gboolean -is_monospaced_element (WebKitDOMElement *element) -{ - gchar *value; - gboolean ret_val = FALSE; - - if (!element) - return FALSE; - - if (!WEBKIT_DOM_IS_HTML_FONT_ELEMENT (element)) - return FALSE; - - value = webkit_dom_element_get_attribute (element, "face"); - - if (g_strcmp0 (value, "monospace") == 0) - ret_val = TRUE; - - g_free (value); - - return ret_val; -} - -/** - * e_html_editor_selection_is_monospaced: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current selection or letter at current cursor position - * is monospaced. - * - * Returns @TRUE when selection is monospaced, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_monospaced (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - selection->priv->is_monospaced = html_editor_selection_is_font_format ( - selection, (IsRightFormatNodeFunc) is_monospaced_element, &selection->priv->is_monospaced); - - return selection->priv->is_monospaced; -} - -static void -monospace_selection (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - WebKitDOMElement *monospaced_element) -{ - gboolean selection_end = FALSE; - gboolean first = TRUE; - gint length, ii; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *sibling, *node, *monospace, *block; - WebKitDOMNodeList *list; - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - block = WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker))); - - monospace = WEBKIT_DOM_NODE (monospaced_element); - node = WEBKIT_DOM_NODE (selection_start_marker); - /* Go through first block in selection. */ - while (block && node && !webkit_dom_node_is_same_node (block, node)) { - if (webkit_dom_node_get_next_sibling (node)) { - /* Prepare the monospaced element. */ - monospace = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - first ? monospace : webkit_dom_node_clone_node (monospace, FALSE), - first ? node : webkit_dom_node_get_next_sibling (node), - NULL); - } else - break; - - /* Move the nodes into monospaced element. */ - while (((sibling = webkit_dom_node_get_next_sibling (monospace)))) { - webkit_dom_node_append_child (monospace, sibling, NULL); - if (webkit_dom_node_is_same_node (WEBKIT_DOM_NODE (selection_end_marker), sibling)) { - selection_end = TRUE; - break; - } - } - - node = webkit_dom_node_get_parent_node (monospace); - first = FALSE; - } - - /* Just one block was selected. */ - if (selection_end) - goto out; - - /* Middle blocks (blocks not containing the end of the selection. */ - block = webkit_dom_node_get_next_sibling (block); - while (block && !selection_end) { - WebKitDOMNode *next_block; - - selection_end = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_end_marker)); - - if (selection_end) - break; - - next_block = webkit_dom_node_get_next_sibling (block); - - monospace = webkit_dom_node_insert_before ( - block, - webkit_dom_node_clone_node (monospace, FALSE), - webkit_dom_node_get_first_child (block), - NULL); - - while (((sibling = webkit_dom_node_get_next_sibling (monospace)))) - webkit_dom_node_append_child (monospace, sibling, NULL); - - block = next_block; - } - - /* Block containing the end of selection. */ - node = WEBKIT_DOM_NODE (selection_end_marker); - while (block && node && !webkit_dom_node_is_same_node (block, node)) { - monospace = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - webkit_dom_node_clone_node (monospace, FALSE), - webkit_dom_node_get_next_sibling (node), - NULL); - - while (((sibling = webkit_dom_node_get_previous_sibling (monospace)))) { - webkit_dom_node_insert_before ( - monospace, - sibling, - webkit_dom_node_get_first_child (monospace), - NULL); - } - - node = webkit_dom_node_get_parent_node (monospace); - } - out: - /* Merge all the monospace elements inside other monospace elements. */ - list = webkit_dom_document_query_selector_all ( - document, "font[face=monospace] > font[face=monospace]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *item; - WebKitDOMNode *child; - - item = webkit_dom_node_list_item (list, ii); - while ((child = webkit_dom_node_get_first_child (item))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (item), - child, - item, - NULL); - } - remove_node (item); - g_object_unref (item); - } - g_object_unref (list); - - /* Merge all the adjacent monospace elements. */ - list = webkit_dom_document_query_selector_all ( - document, "font[face=monospace] + font[face=monospace]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *item; - WebKitDOMNode *child; - - item = webkit_dom_node_list_item (list, ii); - /* The + CSS selector will return some false positives as it doesn't - * take text between elements into account so it will return this: - * <font face="monospace">xx</font>yy<font face="monospace">zz</font> - * as valid, but it isn't so we have to check if previous node - * is indeed element or not. */ - if (WEBKIT_DOM_IS_ELEMENT (webkit_dom_node_get_previous_sibling (item))) { - while ((child = webkit_dom_node_get_first_child (item))) { - webkit_dom_node_append_child ( - webkit_dom_node_get_previous_sibling (item), child, NULL); - } - remove_node (item); - } - g_object_unref (item); - } - g_object_unref (list); - - e_html_editor_selection_restore (selection); -} - -static void -unmonospace_selection (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - WebKitDOMElement *selection_start_marker; - WebKitDOMElement *selection_end_marker; - WebKitDOMElement *selection_start_clone; - WebKitDOMElement *selection_end_clone; - WebKitDOMNode *sibling, *node; - gboolean selection_end = FALSE; - WebKitDOMNode *block, *clone, *monospace; - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - block = WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker))); - - node = WEBKIT_DOM_NODE (selection_start_marker); - monospace = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); - while (monospace && !is_monospaced_element (WEBKIT_DOM_ELEMENT (monospace))) - monospace = webkit_dom_node_get_parent_node (monospace); - - /* No monospaced element was found as a parent of selection start node. */ - if (!monospace) - goto out; - - /* Make a clone of current monospaced element. */ - clone = webkit_dom_node_clone_node (monospace, TRUE); - - /* First block */ - /* Remove all the nodes that are after the selection start point as they - * will be in the cloned node. */ - while (monospace && node && !webkit_dom_node_is_same_node (monospace, node)) { - WebKitDOMNode *tmp; - while (((sibling = webkit_dom_node_get_next_sibling (node)))) - remove_node (sibling); - - tmp = webkit_dom_node_get_parent_node (node); - if (webkit_dom_node_get_next_sibling (node)) - remove_node (node); - node = tmp; - } - - selection_start_clone = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (clone), "#-x-evo-selection-start-marker", NULL); - selection_end_clone = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (clone), "#-x-evo-selection-end-marker", NULL); - - /* No selection start node in the block where it is supposed to be, return. */ - if (!selection_start_clone) - goto out; - - /* Remove all the nodes until we hit the selection start point as these - * nodes will stay monospaced and they are already in original element. */ - node = webkit_dom_node_get_first_child (clone); - while (node) { - WebKitDOMNode *next_sibling; - - next_sibling = webkit_dom_node_get_next_sibling (node); - if (webkit_dom_node_get_first_child (node)) { - if (webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_start_clone))) { - node = webkit_dom_node_get_first_child (node); - continue; - } else - remove_node (node); - } else if (webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_start_clone))) - break; - else - remove_node (node); - - node = next_sibling; - } - - /* Insert the clone into the tree. Do it after the previous clean up. If - * we would do it the other way the line would contain duplicated text nodes - * and the block would be expading and shrinking while we would modify it. */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (monospace), - clone, - webkit_dom_node_get_next_sibling (monospace), - NULL); - - /* Move selection start point the right place. */ - remove_node (WEBKIT_DOM_NODE (selection_start_marker)); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (clone), - WEBKIT_DOM_NODE (selection_start_clone), - clone, - NULL); - - /* Move all the nodes the are supposed to lose the monospace formatting - * out of monospaced element. */ - node = webkit_dom_node_get_first_child (clone); - while (node) { - WebKitDOMNode *next_sibling; - - next_sibling = webkit_dom_node_get_next_sibling (node); - if (webkit_dom_node_get_first_child (node)) { - if (selection_end_clone && - webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_end_clone))) { - node = webkit_dom_node_get_first_child (node); - continue; - } else - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (clone), - node, - clone, - NULL); - } else if (selection_end_clone && - webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_end_clone))) { - selection_end = TRUE; - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (clone), - node, - clone, - NULL); - break; - } else - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (clone), - node, - clone, - NULL); - - node = next_sibling; - } - - if (!webkit_dom_node_get_first_child (clone)) - remove_node (clone); - - /* Just one block was selected and we hit the selection end point. */ - if (selection_end) - goto out; - - /* Middle blocks */ - block = webkit_dom_node_get_next_sibling (block); - while (block && !selection_end) { - WebKitDOMNode *next_block, *child, *parent; - WebKitDOMElement *monospaced_element; - - selection_end = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_end_marker)); - - if (selection_end) - break; - - next_block = webkit_dom_node_get_next_sibling (block); - - /* Find the monospaced element and move all the nodes from it and - * finally remove it. */ - monospaced_element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block), "font[face=monospace]", NULL); - if (!monospaced_element) - break; - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (monospaced_element)); - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (monospaced_element)))) { - webkit_dom_node_insert_before ( - parent, child, WEBKIT_DOM_NODE (monospaced_element), NULL); - } - - remove_node (WEBKIT_DOM_NODE (monospaced_element)); - - block = next_block; - } - - /* End block */ - node = WEBKIT_DOM_NODE (selection_end_marker); - monospace = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_end_marker)); - while (monospace && !is_monospaced_element (WEBKIT_DOM_ELEMENT (monospace))) - monospace = webkit_dom_node_get_parent_node (monospace); - - /* No monospaced element was found as a parent of selection end node. */ - if (!monospace) - return; - - clone = WEBKIT_DOM_NODE (monospace); - node = webkit_dom_node_get_first_child (clone); - /* Move all the nodes that are supposed to lose the monospaced formatting - * out of the monospaced element. */ - while (node) { - WebKitDOMNode *next_sibling; - - next_sibling = webkit_dom_node_get_next_sibling (node); - if (webkit_dom_node_get_first_child (node)) { - if (webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_end_marker))) { - node = webkit_dom_node_get_first_child (node); - continue; - } else - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (clone), - node, - clone, - NULL); - } else if (webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_end_marker))) { - selection_end = TRUE; - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (clone), - node, - clone, - NULL); - break; - } else { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (clone), - node, - clone, - NULL); - } - - node = next_sibling; - } - - if (!webkit_dom_node_get_first_child (clone)) - remove_node (clone); - out: - e_html_editor_selection_restore (selection); -} - -/** - * e_html_editor_selection_set_monospaced: - * @selection: an #EHTMLEditorSelection - * @monospaced: @TRUE to enable monospaced, @FALSE to disable - * - * Toggles monospaced formatting of current selection or letter at current cursor - * position, depending on whether @monospaced is @TRUE or @FALSE. - */ -void -e_html_editor_selection_set_monospaced (EHTMLEditorSelection *selection, - gboolean monospaced) -{ - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMDocument *document; - WebKitDOMRange *range; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (e_html_editor_selection_is_monospaced (selection) == monospaced) - return; - - selection->priv->is_monospaced = monospaced; - - range = html_editor_selection_get_current_range (selection); - if (!range) - return; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_MONOSPACE; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - ev->data.style.from = !monospaced; - ev->data.style.to = monospaced; - } - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - if (monospaced) { - guint font_size; - WebKitDOMElement *monospace; - - monospace = webkit_dom_document_create_element ( - document, "font", NULL); - webkit_dom_element_set_attribute ( - monospace, "face", "monospace", NULL); - - font_size = selection->priv->font_size; - if (font_size != 0) { - gchar *font_size_str; - - font_size_str = g_strdup_printf ("%d", font_size); - webkit_dom_element_set_attribute ( - monospace, "size", font_size_str, NULL); - g_free (font_size_str); - } - - if (!webkit_dom_range_get_collapsed (range, NULL)) - monospace_selection (selection, document, monospace); - else { - /* https://bugs.webkit.org/show_bug.cgi?id=15256 */ - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (monospace), - UNICODE_ZERO_WIDTH_SPACE, - NULL); - webkit_dom_range_insert_node ( - range, WEBKIT_DOM_NODE (monospace), NULL); - - e_html_editor_selection_move_caret_into_element ( - document, monospace, FALSE); - } - } else { - gboolean is_bold, is_italic, is_underline, is_strikethrough; - guint font_size; - WebKitDOMElement *tt_element; - WebKitDOMNode *node; - - node = webkit_dom_range_get_end_container (range, NULL); - if (WEBKIT_DOM_IS_ELEMENT (node) && - is_monospaced_element (WEBKIT_DOM_ELEMENT (node))) { - tt_element = WEBKIT_DOM_ELEMENT (node); - } else { - tt_element = e_html_editor_dom_node_find_parent_element (node, "FONT"); - - if (!is_monospaced_element (tt_element)) { - g_object_unref (view); - g_object_unref (range); - g_object_unref (dom_selection); - g_object_unref (dom_window); - g_free (ev); - return; - } - } - - /* Save current formatting */ - is_bold = selection->priv->is_bold; - is_italic = selection->priv->is_italic; - is_underline = selection->priv->is_underline; - is_strikethrough = selection->priv->is_strikethrough; - font_size = selection->priv->font_size; - - if (!webkit_dom_range_get_collapsed (range, NULL)) - unmonospace_selection (selection, document); - else { - e_html_editor_selection_save (selection); - set_font_style (document, "", FALSE); - e_html_editor_selection_restore (selection); - } - - /* Re-set formatting */ - if (is_bold) - e_html_editor_selection_set_bold (selection, TRUE); - if (is_italic) - e_html_editor_selection_set_italic (selection, TRUE); - if (is_underline) - e_html_editor_selection_set_underline (selection, TRUE); - if (is_strikethrough) - e_html_editor_selection_set_strikethrough (selection, TRUE); - - if (font_size) - e_html_editor_selection_set_font_size (selection, font_size); - } - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - g_object_unref (range); - g_object_unref (dom_selection); - g_object_unref (dom_window); - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "monospaced"); -} - -static gboolean -is_strikethrough_element (WebKitDOMElement *element) -{ - if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - return element_has_tag (element, "strike"); -} - -/** - * e_html_editor_selection_is_strikethrough: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current selection or letter at current cursor position - * is striked through. - * - * Returns @TRUE when selection is striked through, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_strikethrough (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - selection->priv->is_strikethrough = html_editor_selection_is_font_format ( - selection, (IsRightFormatNodeFunc) is_strikethrough_element, &selection->priv->is_strikethrough); - - return selection->priv->is_strikethrough; -} - -/** - * e_html_editor_selection_set_strikethrough: - * @selection: an #EHTMLEditorSelection - * @strikethrough: @TRUE to enable strikethrough, @FALSE to disable - * - * Toggles strike through formatting of current selection or letter at current - * cursor position, depending on whether @strikethrough is @TRUE or @FALSE. - */ -void -e_html_editor_selection_set_strikethrough (EHTMLEditorSelection *selection, - gboolean strikethrough) -{ - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (e_html_editor_selection_is_strikethrough (selection) == strikethrough) - return; - - selection->priv->is_strikethrough = strikethrough; - - html_editor_selection_set_font_style ( - selection, E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, strikethrough); - - g_object_notify (G_OBJECT (selection), "strikethrough"); -} - -static gboolean -is_subscript_element (WebKitDOMElement *element) -{ - if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - return element_has_tag (element, "sub"); -} - -/** - * e_html_editor_selection_is_subscript: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current selection or letter at current cursor position - * is in subscript. - * - * Returns @TRUE when selection is in subscript, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_subscript (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - return html_editor_selection_is_font_format ( - selection, (IsRightFormatNodeFunc) is_subscript_element, NULL); -} - -/** - * e_html_editor_selection_set_subscript: - * @selection: an #EHTMLEditorSelection - * @subscript: @TRUE to enable subscript, @FALSE to disable - * - * Toggles subscript of current selection or letter at current cursor position, - * depending on whether @subscript is @TRUE or @FALSE. - */ -void -e_html_editor_selection_set_subscript (EHTMLEditorSelection *selection, - gboolean subscript) -{ - EHTMLEditorView *view; - EHTMLEditorViewCommand command; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (e_html_editor_selection_is_subscript (selection) == subscript) - return; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - command = E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT; - e_html_editor_view_exec_command (view, command, NULL); - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "subscript"); -} - -static gboolean -is_superscript_element (WebKitDOMElement *element) -{ - if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - return element_has_tag (element, "sup"); -} - -/** - * e_html_editor_selection_is_superscript: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current selection or letter at current cursor position - * is in superscript. - * - * Returns @TRUE when selection is in superscript, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_superscript (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - return html_editor_selection_is_font_format ( - selection, (IsRightFormatNodeFunc) is_superscript_element, NULL); -} - -/** - * e_html_editor_selection_set_superscript: - * @selection: an #EHTMLEditorSelection - * @superscript: @TRUE to enable superscript, @FALSE to disable - * - * Toggles superscript of current selection or letter at current cursor position, - * depending on whether @superscript is @TRUE or @FALSE. - */ -void -e_html_editor_selection_set_superscript (EHTMLEditorSelection *selection, - gboolean superscript) -{ - EHTMLEditorView *view; - EHTMLEditorViewCommand command; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (e_html_editor_selection_is_superscript (selection) == superscript) - return; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - command = E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT; - e_html_editor_view_exec_command (view, command, NULL); - - g_object_unref (view); - - g_object_notify (G_OBJECT (selection), "superscript"); -} - -static gboolean -is_underline_element (WebKitDOMElement *element) -{ - if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - return element_has_tag (element, "u"); -} - -/** - * e_html_editor_selection_is_underline: - * @selection: an #EHTMLEditorSelection - * - * Returns whether current selection or letter at current cursor position - * is underlined. - * - * Returns @TRUE when selection is underlined, @FALSE otherwise. - */ -gboolean -e_html_editor_selection_is_underline (EHTMLEditorSelection *selection) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE); - - selection->priv->is_underline = html_editor_selection_is_font_format ( - selection, (IsRightFormatNodeFunc) is_underline_element, &selection->priv->is_underline); - - return selection->priv->is_underline; -} - -/** - * e_html_editor_selection_set_underline: - * @selection: an #EHTMLEditorSelection - * @underline: @TRUE to enable underline, @FALSE to disable - * - * Toggles underline formatting of current selection or letter at current - * cursor position, depending on whether @underline is @TRUE or @FALSE. - */ -void -e_html_editor_selection_set_underline (EHTMLEditorSelection *selection, - gboolean underline) -{ - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - if (e_html_editor_selection_is_underline (selection) == underline) - return; - - selection->priv->is_underline = underline; - - html_editor_selection_set_font_style ( - selection, E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, underline); - - g_object_notify (G_OBJECT (selection), "underline"); -} - -/** - * e_html_editor_selection_unlink: - * @selection: an #EHTMLEditorSelection - * - * Removes any links (<A> elements) from current selection or at current - * cursor position. - */ -void -e_html_editor_selection_unlink (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - gchar *text; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - WebKitDOMElement *link; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - g_object_unref (view); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - link = e_html_editor_dom_node_find_parent_element ( - webkit_dom_range_get_start_container (range, NULL), "A"); - - g_object_unref (dom_selection); - g_object_unref (dom_window); - - if (!link) { - WebKitDOMNode *node; - - /* get element that was clicked on */ - node = webkit_dom_range_get_common_ancestor_container (range, NULL); - if (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) { - link = e_html_editor_dom_node_find_parent_element (node, "A"); - if (link && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link)) { - g_object_unref (range); - return; - } - } else - link = WEBKIT_DOM_ELEMENT (node); - } - - g_object_unref (range); - - if (!link) - return; - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - EHTMLEditorViewHistoryEvent *ev; - WebKitDOMDocumentFragment *fragment; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_REMOVE_LINK; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - fragment = webkit_dom_document_create_document_fragment (document); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (link), TRUE), - NULL); - ev->data.fragment = fragment; - - e_html_editor_view_insert_new_history_event (view, ev); - } - - text = webkit_dom_html_element_get_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (link)); - webkit_dom_html_element_set_outer_html ( - WEBKIT_DOM_HTML_ELEMENT (link), text, NULL); - - g_free (text); -} - -/** - * e_html_editor_selection_create_link: - * @selection: an #EHTMLEditorSelection - * @uri: destination of the new link - * - * Converts current selection into a link pointing to @url. - */ -void -e_html_editor_selection_create_link (EHTMLEditorSelection *selection, - const gchar *uri) -{ - EHTMLEditorView *view; - EHTMLEditorViewCommand command; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (uri != NULL && *uri != '\0'); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - command = E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK; - e_html_editor_view_exec_command (view, command, uri); - - g_object_unref (view); -} - -/** - * e_html_editor_selection_insert_text: - * @selection: an #EHTMLEditorSelection - * @plain_text: text to insert - * - * Inserts @plain_text at current cursor position. When a text range is selected, - * it will be replaced by @plain_text. - */ -void -e_html_editor_selection_insert_text (EHTMLEditorSelection *selection, - const gchar *plain_text) -{ - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (plain_text != NULL); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - gboolean collapsed; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_PASTE; - - collapsed = e_html_editor_selection_is_collapsed (selection); - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - if (!collapsed) { - ev->before.end.x = ev->before.start.x; - ev->before.end.y = ev->before.start.y; - } - ev->data.string.from = NULL; - ev->data.string.to = g_strdup (plain_text); - } - - e_html_editor_view_convert_and_insert_plain_text (view, plain_text); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_view_set_changed (view, TRUE); - - g_object_unref (view); -} - -static gboolean -pasting_quoted_content (const gchar *content) -{ - /* Check if the content we are pasting is a quoted content from composer. - * If it is, we can't use WebKit to paste it as it would leave the formatting - * on the content. */ - return g_str_has_prefix ( - content, - "<meta http-equiv=\"content-type\" content=\"text/html; " - "charset=utf-8\"><blockquote type=\"cite\"") && - strstr (content, "\"-x-evo-"); -} - -/** - * e_html_editor_selection_insert_html: - * @selection: an #EHTMLEditorSelection - * @html_text: an HTML code to insert - * - * Insert @html_text into document at current cursor position. When a text range - * is selected, it will be replaced by @html_text. - */ -void -e_html_editor_selection_insert_html (EHTMLEditorSelection *selection, - const gchar *html_text) -{ - EHTMLEditorView *view; - EHTMLEditorViewCommand command; - EHTMLEditorViewHistoryEvent *ev = NULL; - gboolean html_mode; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (html_text != NULL); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - gboolean collapsed; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_INSERT_HTML; - - collapsed = e_html_editor_selection_is_collapsed (selection); - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - if (!collapsed) { - ev->before.end.x = ev->before.start.x; - ev->before.end.y = ev->before.start.y; - } - ev->data.string.from = NULL; - ev->data.string.to = g_strdup (html_text); - } - - html_mode = e_html_editor_view_get_html_mode (view); - command = E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML; - if (html_mode || - (e_html_editor_view_is_pasting_content_from_itself (view) && - !pasting_quoted_content (html_text))) { - if (!e_html_editor_selection_is_collapsed (selection)) { - EHTMLEditorViewHistoryEvent *event; - WebKitDOMDocumentFragment *fragment; - WebKitDOMRange *range; - - event = g_new0 (EHTMLEditorViewHistoryEvent, 1); - event->type = HISTORY_DELETE; - - range = html_editor_selection_get_current_range (selection); - fragment = webkit_dom_range_clone_contents (range, NULL); - g_object_unref (range); - event->data.fragment = fragment; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &event->before.start.x, - &event->before.start.y, - &event->before.end.x, - &event->before.end.y); - - event->after.start.x = event->before.start.x; - event->after.start.y = event->before.start.y; - event->after.end.x = event->before.start.x; - event->after.end.y = event->before.start.y; - - e_html_editor_view_insert_new_history_event (view, event); - - event = g_new0 (EHTMLEditorViewHistoryEvent, 1); - event->type = HISTORY_AND; - - e_html_editor_view_insert_new_history_event (view, event); - } - - e_html_editor_view_exec_command (view, command, html_text); - e_html_editor_view_fix_file_uri_images (view); - if (strstr (html_text, "id=\"-x-evo-selection-start-marker\"")) - e_html_editor_selection_restore (selection); - - if (!html_mode) { - WebKitDOMDocument *document; - WebKitDOMNodeList *list; - gint ii, length; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - list = webkit_dom_document_query_selector_all ( - document, "span[style^=font-family]", NULL); - length = webkit_dom_node_list_get_length (list); - if (length > 0) - e_html_editor_selection_save (selection); - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *span, *child; - - span = webkit_dom_node_list_item (list, ii); - while ((child = webkit_dom_node_get_first_child (span))) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (span), - child, - span, - NULL); - - remove_node (span); - g_object_unref (span); - } - g_object_unref (list); - - if (length > 0) - e_html_editor_selection_restore (selection); - } - - e_html_editor_view_check_magic_links (view, FALSE); - e_html_editor_view_force_spell_check_in_viewport (view); - - e_html_editor_selection_scroll_to_caret (selection); - } else - e_html_editor_view_convert_and_insert_html_to_plain_text ( - view, html_text); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_view_set_changed (view, TRUE); - - g_object_unref (view); -} - -void -e_html_editor_selection_insert_as_text (EHTMLEditorSelection *selection, - const gchar *html_text) -{ - EHTMLEditorView *view; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (html_text != NULL); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - e_html_editor_view_convert_and_insert_html_to_plain_text (view, html_text); - - g_object_unref (view); -} - -/************************* image_load_and_insert_async() *************************/ - -typedef struct _LoadContext LoadContext; - -struct _LoadContext { - EHTMLEditorSelection *selection; - WebKitDOMElement *element; - GInputStream *input_stream; - GOutputStream *output_stream; - GFile *file; - GFileInfo *file_info; - goffset total_num_bytes; - gssize bytes_read; - const gchar *content_type; - const gchar *filename; - gchar buffer[4096]; -}; - -/* Forward Declaration */ -static void -image_load_stream_read_cb (GInputStream *input_stream, - GAsyncResult *result, - LoadContext *load_context); - -static LoadContext * -image_load_context_new (EHTMLEditorSelection *selection) -{ - LoadContext *load_context; - - load_context = g_slice_new0 (LoadContext); - load_context->selection = selection; - - return load_context; -} - -static void -image_load_context_free (LoadContext *load_context) -{ - if (load_context->input_stream != NULL) - g_object_unref (load_context->input_stream); - - if (load_context->output_stream != NULL) - g_object_unref (load_context->output_stream); - - if (load_context->file_info != NULL) - g_object_unref (load_context->file_info); - - if (load_context->file != NULL) - g_object_unref (load_context->file); - - g_slice_free (LoadContext, load_context); -} - -static void -replace_base64_image_src (EHTMLEditorSelection *selection, - WebKitDOMElement *element, - const gchar *base64_content, - const gchar *filename, - const gchar *uri) -{ - EHTMLEditorView *view; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - e_html_editor_view_set_changed (view, TRUE); - g_object_unref (view); - - if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (element)) - webkit_dom_html_image_element_set_src ( - WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), - base64_content); - else - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (element), - "background", - base64_content, - NULL); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (element), "data-name", - filename ? filename : "", NULL); -} - -static void -insert_base64_image (EHTMLEditorSelection *selection, - const gchar *base64_content, - const gchar *filename, - const gchar *uri) -{ - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMDocument *document; - WebKitDOMElement *element, *selection_start_marker, *resizable_wrapper; - WebKitDOMText *text; - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - e_html_editor_view_set_changed (view, TRUE); - - if (!e_html_editor_selection_is_collapsed (selection)) { - EHTMLEditorViewHistoryEvent *ev; - WebKitDOMDocumentFragment *fragment; - WebKitDOMRange *range; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_DELETE; - - range = html_editor_selection_get_current_range (selection); - fragment = webkit_dom_range_clone_contents (range, NULL); - g_object_unref (range); - ev->data.fragment = fragment; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - ev->after.start.x = ev->before.start.x; - ev->after.start.y = ev->before.start.y; - ev->after.end.x = ev->before.start.x; - ev->after.end.y = ev->before.start.y; - - e_html_editor_view_insert_new_history_event (view, ev); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_AND; - - e_html_editor_view_insert_new_history_event (view, ev); - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL); - } - - e_html_editor_selection_save (selection); - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_IMAGE; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - } - - resizable_wrapper = - webkit_dom_document_create_element (document, "span", NULL); - webkit_dom_element_set_attribute ( - resizable_wrapper, "class", "-x-evo-resizable-wrapper", NULL); - - element = webkit_dom_document_create_element (document, "img", NULL); - webkit_dom_html_image_element_set_src ( - WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), - base64_content); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (element), "data-name", - filename ? filename : "", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (resizable_wrapper), - WEBKIT_DOM_NODE (element), - NULL); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)), - WEBKIT_DOM_NODE (resizable_wrapper), - WEBKIT_DOM_NODE (selection_start_marker), - NULL); - - /* We have to again use UNICODE_ZERO_WIDTH_SPACE character to restore - * caret on right position */ - text = webkit_dom_document_create_text_node ( - document, UNICODE_ZERO_WIDTH_SPACE); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)), - WEBKIT_DOM_NODE (text), - WEBKIT_DOM_NODE (selection_start_marker), - NULL); - - if (ev) { - WebKitDOMDocumentFragment *fragment; - WebKitDOMNode *node; - - fragment = webkit_dom_document_create_document_fragment (document); - node = webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (resizable_wrapper), TRUE), - NULL); - webkit_dom_html_element_insert_adjacent_html ( - WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "​", NULL); - ev->data.fragment = fragment; - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_for_current_paragraph (view); - e_html_editor_selection_scroll_to_caret (selection); - - g_object_unref (view); -} - -static void -image_load_finish (LoadContext *load_context) -{ - EHTMLEditorSelection *selection; - GMemoryOutputStream *output_stream; - gchar *base64_encoded, *mime_type, *output, *uri; - gsize size; - gpointer data; - - output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream); - - selection = load_context->selection; - - mime_type = g_content_type_get_mime_type (load_context->content_type); - - data = g_memory_output_stream_get_data (output_stream); - size = g_memory_output_stream_get_data_size (output_stream); - uri = g_file_get_uri (load_context->file); - - base64_encoded = g_base64_encode ((const guchar *) data, size); - output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL); - if (load_context->element) - replace_base64_image_src ( - selection, load_context->element, output, load_context->filename, uri); - else - insert_base64_image (selection, output, load_context->filename, uri); - - g_free (base64_encoded); - g_free (output); - g_free (mime_type); - g_free (uri); - - image_load_context_free (load_context); -} - -static void -image_load_write_cb (GOutputStream *output_stream, - GAsyncResult *result, - LoadContext *load_context) -{ - GInputStream *input_stream; - gssize bytes_written; - GError *error = NULL; - - bytes_written = g_output_stream_write_finish ( - output_stream, result, &error); - - if (error) { - image_load_context_free (load_context); - return; - } - - input_stream = load_context->input_stream; - - if (bytes_written < load_context->bytes_read) { - g_memmove ( - load_context->buffer, - load_context->buffer + bytes_written, - load_context->bytes_read - bytes_written); - load_context->bytes_read -= bytes_written; - - g_output_stream_write_async ( - output_stream, - load_context->buffer, - load_context->bytes_read, - G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback) image_load_write_cb, - load_context); - } else - g_input_stream_read_async ( - input_stream, - load_context->buffer, - sizeof (load_context->buffer), - G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback) image_load_stream_read_cb, - load_context); -} - -static void -image_load_stream_read_cb (GInputStream *input_stream, - GAsyncResult *result, - LoadContext *load_context) -{ - GOutputStream *output_stream; - gssize bytes_read; - GError *error = NULL; - - bytes_read = g_input_stream_read_finish ( - input_stream, result, &error); - - if (error) { - image_load_context_free (load_context); - return; - } - - if (bytes_read == 0) { - image_load_finish (load_context); - return; - } - - output_stream = load_context->output_stream; - load_context->bytes_read = bytes_read; - - g_output_stream_write_async ( - output_stream, - load_context->buffer, - load_context->bytes_read, - G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback) image_load_write_cb, - load_context); -} - -static void -image_load_file_read_cb (GFile *file, - GAsyncResult *result, - LoadContext *load_context) -{ - GFileInputStream *input_stream; - GOutputStream *output_stream; - GError *error = NULL; - - /* Input stream might be NULL, so don't use cast macro. */ - input_stream = g_file_read_finish (file, result, &error); - load_context->input_stream = (GInputStream *) input_stream; - - if (error) { - image_load_context_free (load_context); - return; - } - - /* Load the contents into a GMemoryOutputStream. */ - output_stream = g_memory_output_stream_new ( - NULL, 0, g_realloc, g_free); - - load_context->output_stream = output_stream; - - g_input_stream_read_async ( - load_context->input_stream, - load_context->buffer, - sizeof (load_context->buffer), - G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback) image_load_stream_read_cb, - load_context); -} - -static void -image_load_query_info_cb (GFile *file, - GAsyncResult *result, - LoadContext *load_context) -{ - GFileInfo *file_info; - GError *error = NULL; - - file_info = g_file_query_info_finish (file, result, &error); - if (error) { - image_load_context_free (load_context); - return; - } - - load_context->content_type = g_file_info_get_content_type (file_info); - load_context->total_num_bytes = g_file_info_get_size (file_info); - load_context->filename = g_file_info_get_name (file_info); - - g_file_read_async ( - file, G_PRIORITY_DEFAULT, - NULL, (GAsyncReadyCallback) - image_load_file_read_cb, load_context); -} - -static void -image_load_and_insert_async (EHTMLEditorSelection *selection, - WebKitDOMElement *element, - const gchar *uri) -{ - LoadContext *load_context; - GFile *file; - - g_return_if_fail (uri && *uri); - - file = g_file_new_for_uri (uri); - g_return_if_fail (file != NULL); - - load_context = image_load_context_new (selection); - load_context->file = file; - load_context->element = element; - - g_file_query_info_async ( - file, "standard::*", - G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT, - NULL, (GAsyncReadyCallback) - image_load_query_info_cb, load_context); -} - -/** - * e_html_editor_selection_insert_image: - * @selection: an #EHTMLEditorSelection - * @image_uri: an URI of the source image - * - * Inserts image at current cursor position using @image_uri as source. When a - * text range is selected, it will be replaced by the image. - */ -void -e_html_editor_selection_insert_image (EHTMLEditorSelection *selection, - const gchar *image_uri) -{ - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (image_uri != NULL); - - if (is_in_html_mode (selection)) { - if (strstr (image_uri, ";base64,")) { - if (g_str_has_prefix (image_uri, "data:")) - insert_base64_image (selection, image_uri, "", ""); - if (strstr (image_uri, ";data")) { - const gchar *base64_data = strstr (image_uri, ";") + 1; - gchar *filename; - glong filename_length; - - filename_length = - g_utf8_strlen (image_uri, -1) - - g_utf8_strlen (base64_data, -1) - 1; - filename = g_strndup (image_uri, filename_length); - - insert_base64_image (selection, base64_data, filename, ""); - g_free (filename); - } - } else - image_load_and_insert_async (selection, NULL, image_uri); - } -} - -/** - * e_html_editor_selection_replace_image_src: - * @selection: an #EHTMLEditorSelection - * @element: #WebKitDOMElement element - * @image_uri: an URI of the source image - * - * If given @element is image we will replace the src attribute of it with base64 - * data from given @image_uri. Otherwise we will set the base64 data to - * the background attribute of given @element. - */ -void -e_html_editor_selection_replace_image_src (EHTMLEditorSelection *selection, - WebKitDOMElement *element, - const gchar *image_uri) -{ - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - g_return_if_fail (image_uri != NULL); - g_return_if_fail (element && WEBKIT_DOM_IS_ELEMENT (element)); - - if (strstr (image_uri, ";base64,")) { - if (g_str_has_prefix (image_uri, "data:")) - replace_base64_image_src ( - selection, element, image_uri, "", ""); - if (strstr (image_uri, ";data")) { - const gchar *base64_data = strstr (image_uri, ";") + 1; - gchar *filename; - glong filename_length; - - filename_length = - g_utf8_strlen (image_uri, -1) - - g_utf8_strlen (base64_data, -1) - 1; - filename = g_strndup (image_uri, filename_length); - - replace_base64_image_src ( - selection, element, base64_data, filename, ""); - g_free (filename); - } - } else - image_load_and_insert_async (selection, element, image_uri); -} - -void -e_html_editor_selection_move_caret_into_element (WebKitDOMDocument *document, - WebKitDOMElement *element, - gboolean to_start) -{ - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *new_range; - - if (!element) - return; - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - new_range = webkit_dom_document_create_range (document); - - webkit_dom_range_select_node_contents ( - new_range, WEBKIT_DOM_NODE (element), NULL); - webkit_dom_range_collapse (new_range, to_start, NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, new_range); - g_object_unref (new_range); - g_object_unref (dom_selection); - g_object_unref (dom_window); -} - -static gint -find_where_to_break_line (WebKitDOMCharacterData *node, - gint max_length) -{ - gboolean last_break_position_is_dash = FALSE; - gchar *str, *text_start; - gunichar uc; - gint pos = 1, last_break_position = 0, ret_val = 0; - - text_start = webkit_dom_character_data_get_data (node); - - str = text_start; - do { - uc = g_utf8_get_char (str); - if (!uc) { - ret_val = pos <= max_length ? pos : last_break_position > 0 ? last_break_position - 1 : 0; - goto out; - } - - if ((g_unichar_isspace (uc) && !(g_unichar_break_type (uc) == G_UNICODE_BREAK_NON_BREAKING_GLUE)) || - *str == '-') { - if ((last_break_position_is_dash = *str == '-')) { - /* There was no space before the dash */ - if (pos - 1 != last_break_position) { - gchar *rest; - - rest = g_utf8_next_char (str); - if (rest && *rest) { - gunichar next_char; - - /* There is no space after the dash */ - next_char = g_utf8_get_char (rest); - if (g_unichar_isspace (next_char)) - last_break_position_is_dash = FALSE; - else - last_break_position = pos; - } else - last_break_position_is_dash = FALSE; - } else - last_break_position_is_dash = FALSE; - } else - last_break_position = pos; - } - - if ((pos == max_length)) { - /* Look one character after the limit to check if there - * is a space (skip dash) that we are allowed to break at, if so - * break it there. */ - if (*str) { - str = g_utf8_next_char (str); - uc = g_utf8_get_char (str); - - if ((g_unichar_isspace (uc) && - !(g_unichar_break_type (uc) == G_UNICODE_BREAK_NON_BREAKING_GLUE))) - last_break_position = ++pos; - } - break; - } - - pos++; - str = g_utf8_next_char (str); - } while (*str); - - if (last_break_position != 0) - ret_val = last_break_position - 1; - out: - g_free (text_start); - - /* Always break after the dash character. */ - if (last_break_position_is_dash) - ret_val++; - - /* No character to break at is found. We should split at max_length, but - * we will leave the decision on caller as it depends on context. */ - if (ret_val == 0 && last_break_position == 0) - ret_val = -1; - - return ret_val; -} - -static void -mark_and_remove_trailing_space (WebKitDOMDocument *document, - WebKitDOMNode *node) -{ - WebKitDOMElement *element; - - element = webkit_dom_document_create_element (document, "SPAN", NULL); - webkit_dom_element_set_attribute (element, "data-hidden-space", "", NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - webkit_dom_node_get_next_sibling (node), - NULL); - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (node), - webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)), - 1, - "", - NULL); -} - -static void -mark_and_remove_leading_space (WebKitDOMDocument *document, - WebKitDOMNode *node) -{ - WebKitDOMElement *element; - - element = webkit_dom_document_create_element (document, "SPAN", NULL); - webkit_dom_element_set_attribute (element, "data-hidden-space", "", NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - node, - NULL); - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL); -} - -static WebKitDOMElement * -wrap_lines (EHTMLEditorSelection *selection, - WebKitDOMNode *block, - WebKitDOMDocument *document, - gboolean remove_all_br, - gint length_to_wrap, - gint word_wrap_length) -{ - WebKitDOMNode *node, *start_node, *block_clone; - guint line_length; - gulong length_left; - gchar *text_content; - gboolean compensated = FALSE; - gboolean check_next_node = FALSE; - - if (selection) { - gint ii, length; - WebKitDOMDocumentFragment *fragment; - WebKitDOMNodeList *list; - WebKitDOMRange *range; - - range = html_editor_selection_get_current_range (selection); - fragment = webkit_dom_range_clone_contents (range, NULL); - g_object_unref (range); - - /* Select all BR elements or just ours that are used for wrapping. - * We are not removing user BR elements when this function is activated - * from Format->Wrap Lines action */ - list = webkit_dom_document_fragment_query_selector_all ( - fragment, - remove_all_br ? "br" : "br.-x-evo-wrap-br", - NULL); - length = webkit_dom_node_list_get_length (list); - /* And remove them */ - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, length); - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - list = webkit_dom_document_fragment_query_selector_all ( - fragment, "span[data-hidden-space]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *hidden_space_node; - - hidden_space_node = webkit_dom_node_list_item (list, ii); - webkit_dom_html_element_set_outer_text ( - WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL); - g_object_unref (hidden_space_node); - } - g_object_unref (list); - - node = WEBKIT_DOM_NODE (fragment); - start_node = node; - } else { - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *start_point = NULL, *first_child; - - if (!webkit_dom_node_has_child_nodes (block)) - return WEBKIT_DOM_ELEMENT (block); - - /* Avoid wrapping when the block contains just the BR element alone - * or with selection markers. */ - if ((first_child = webkit_dom_node_get_first_child (block)) && - WEBKIT_DOM_IS_HTMLBR_ELEMENT (first_child)) { - WebKitDOMNode *next_sibling; - - if ((next_sibling = webkit_dom_node_get_next_sibling (first_child))) { - if (e_html_editor_node_is_selection_position_node (next_sibling) && - (next_sibling = webkit_dom_node_get_next_sibling (next_sibling)) && - e_html_editor_node_is_selection_position_node (next_sibling) && - !webkit_dom_node_get_next_sibling (next_sibling)) - return WEBKIT_DOM_ELEMENT (block); - } else - return WEBKIT_DOM_ELEMENT (block); - } - - block_clone = webkit_dom_node_clone_node (block, TRUE); - /* When we wrap, we are wrapping just the text after caret, text - * before the caret is already wrapped, so unwrap the text after - * the caret position */ - selection_end_marker = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block_clone), - "span#-x-evo-selection-end-marker", - NULL); - - if (selection_end_marker) { - WebKitDOMNode *nd = WEBKIT_DOM_NODE (selection_end_marker); - - while (nd) { - WebKitDOMNode *parent_node; - WebKitDOMNode *next_nd = webkit_dom_node_get_next_sibling (nd); - - parent_node = webkit_dom_node_get_parent_node (nd); - if (!next_nd && parent_node && !webkit_dom_node_is_same_node (parent_node, block_clone)) - next_nd = webkit_dom_node_get_next_sibling (parent_node); - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (nd)) { - if (remove_all_br) - remove_node (nd); - else if (element_has_class (WEBKIT_DOM_ELEMENT (nd), "-x-evo-wrap-br")) - remove_node (nd); - } else if (WEBKIT_DOM_IS_ELEMENT (nd) && - webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd), "data-hidden-space")) - webkit_dom_html_element_set_outer_text ( - WEBKIT_DOM_HTML_ELEMENT (nd), " ", NULL); - - nd = next_nd; - } - } else { - gint ii, length; - WebKitDOMNodeList *list; - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (block_clone), "span[data-hidden-space]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *hidden_space_node; - - hidden_space_node = webkit_dom_node_list_item (list, ii); - webkit_dom_html_element_set_outer_text ( - WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL); - g_object_unref (hidden_space_node); - } - g_object_unref (list); - } - - /* We have to start from the end of the last wrapped line */ - selection_start_marker = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block_clone), - "span#-x-evo-selection-start-marker", - NULL); - - if (selection_start_marker) { - gboolean first_removed = FALSE; - WebKitDOMNode *nd; - - nd = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_start_marker)); - while (nd) { - WebKitDOMNode *prev_nd = webkit_dom_node_get_previous_sibling (nd); - - if (!prev_nd && !webkit_dom_node_is_same_node (webkit_dom_node_get_parent_node (nd), block_clone)) - prev_nd = webkit_dom_node_get_previous_sibling (webkit_dom_node_get_parent_node (nd)); - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (nd)) { - if (first_removed) { - start_point = nd; - break; - } else { - remove_node (nd); - first_removed = TRUE; - } - } else if (WEBKIT_DOM_IS_ELEMENT (nd) && - webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd), "data-hidden-space")) { - webkit_dom_html_element_set_outer_text ( - WEBKIT_DOM_HTML_ELEMENT (nd), " ", NULL); - } else if (!prev_nd) { - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node (nd); - if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) - start_point = nd; - } - - nd = prev_nd; - } - } - - webkit_dom_node_normalize (block_clone); - node = webkit_dom_node_get_first_child (block_clone); - if (node) { - text_content = webkit_dom_node_get_text_content (node); - if (g_strcmp0 ("\n", text_content) == 0) - node = webkit_dom_node_get_next_sibling (node); - g_free (text_content); - } - - if (start_point) { - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (start_point)) - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_point)); - else - node = start_point; - start_node = block_clone; - } else - start_node = node; - } - - line_length = 0; - while (node) { - gint offset = 0; - WebKitDOMElement *element; - - if (WEBKIT_DOM_IS_TEXT (node)) { - const gchar *newline; - WebKitDOMNode *next_sibling; - - /* If there is temporary hidden space we remove it */ - text_content = webkit_dom_node_get_text_content (node); - if (strstr (text_content, UNICODE_ZERO_WIDTH_SPACE)) { - if (g_str_has_prefix (text_content, UNICODE_ZERO_WIDTH_SPACE)) - webkit_dom_character_data_delete_data ( - WEBKIT_DOM_CHARACTER_DATA (node), - 0, - 1, - NULL); - if (g_str_has_suffix (text_content, UNICODE_ZERO_WIDTH_SPACE)) - webkit_dom_character_data_delete_data ( - WEBKIT_DOM_CHARACTER_DATA (node), - g_utf8_strlen (text_content, -1) - 1, - 1, - NULL); - g_free (text_content); - text_content = webkit_dom_node_get_text_content (node); - } - newline = strstr (text_content, "\n"); - - next_sibling = node; - while (newline) { - next_sibling = WEBKIT_DOM_NODE (webkit_dom_text_split_text ( - WEBKIT_DOM_TEXT (next_sibling), - g_utf8_pointer_to_offset (text_content, newline), - NULL)); - - if (!next_sibling) - break; - - element = webkit_dom_document_create_element ( - document, "BR", NULL); - element_add_class (element, "-x-evo-temp-wrap-text-br"); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (next_sibling), - WEBKIT_DOM_NODE (element), - next_sibling, - NULL); - - g_free (text_content); - - text_content = webkit_dom_node_get_text_content (next_sibling); - if (g_str_has_prefix (text_content, "\n")) { - webkit_dom_character_data_delete_data ( - WEBKIT_DOM_CHARACTER_DATA (next_sibling), 0, 1, NULL); - g_free (text_content); - text_content = - webkit_dom_node_get_text_content (next_sibling); - } - newline = strstr (text_content, "\n"); - } - g_free (text_content); - } else { - if (e_html_editor_node_is_selection_position_node (node)) { - if (line_length == 0) { - WebKitDOMNode *tmp_node; - - tmp_node = webkit_dom_node_get_previous_sibling (node); - /* Only check if there is some node before the selection marker. */ - if (tmp_node && !e_html_editor_node_is_selection_position_node (tmp_node)) - check_next_node = TRUE; - } - node = webkit_dom_node_get_next_sibling (node); - continue; - } - - check_next_node = FALSE; - /* If element is ANCHOR we wrap it separately */ - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) { - glong anchor_length; - WebKitDOMNode *next_sibling; - - text_content = webkit_dom_node_get_text_content (node); - anchor_length = g_utf8_strlen (text_content, -1); - g_free (text_content); - - next_sibling = webkit_dom_node_get_next_sibling (node); - /* If the anchor doesn't fit on the line move the inner - * nodes out of it and start to wrap them. */ - if ((line_length + anchor_length) > length_to_wrap) { - WebKitDOMNode *inner_node; - - while ((inner_node = webkit_dom_node_get_first_child (node))) { - g_object_set_data ( - G_OBJECT (inner_node), - "-x-evo-anchor-text", - GINT_TO_POINTER (1)); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - inner_node, - next_sibling, - NULL); - } - next_sibling = webkit_dom_node_get_next_sibling (node); - remove_node (node); - node = next_sibling; - continue; - } - - line_length += anchor_length; - node = next_sibling; - continue; - } - - if (element_has_class (WEBKIT_DOM_ELEMENT (node), "Apple-tab-span")) { - WebKitDOMNode *sibling; - gint tab_length; - - sibling = webkit_dom_node_get_previous_sibling (node); - if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (sibling), "Apple-tab-span")) - tab_length = TAB_LENGTH; - else { - tab_length = TAB_LENGTH - (line_length + compensated ? 0 : (word_wrap_length - length_to_wrap)) % TAB_LENGTH; - compensated = TRUE; - } - - if (line_length + tab_length > length_to_wrap) { - if (webkit_dom_node_get_next_sibling (node)) { - element = webkit_dom_document_create_element ( - document, "BR", NULL); - element_add_class (element, "-x-evo-wrap-br"); - node = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - webkit_dom_node_get_next_sibling (node), - NULL); - } - line_length = 0; - compensated = FALSE; - } else - line_length += tab_length; - - sibling = webkit_dom_node_get_next_sibling (node); - node = sibling; - continue; - } - /* When we are not removing user-entered BR elements (lines wrapped by user), - * we need to skip those elements */ - if (!remove_all_br && WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) { - if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) { - line_length = 0; - compensated = FALSE; - node = webkit_dom_node_get_next_sibling (node); - continue; - } - } - goto next_node; - } - - /* If length of this node + what we already have is still less - * then length_to_wrap characters, then just concatenate it and - * continue to next node */ - length_left = webkit_dom_character_data_get_length ( - WEBKIT_DOM_CHARACTER_DATA (node)); - - if ((length_left + line_length) <= length_to_wrap) { - if (check_next_node) - goto check_node; - line_length += length_left; - if (line_length == length_to_wrap) { - line_length = 0; - - element = webkit_dom_document_create_element (document, "BR", NULL); - element_add_class (element, "-x-evo-wrap-br"); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - webkit_dom_node_get_next_sibling (node), - NULL); - } - goto next_node; - } - - /* wrap until we have something */ - while (node && (length_left + line_length) > length_to_wrap) { - gboolean insert_and_continue; - gint max_length; - - check_node: - insert_and_continue = FALSE; - - if (!WEBKIT_DOM_IS_CHARACTER_DATA (node)) - goto next_node; - - element = webkit_dom_document_create_element (document, "BR", NULL); - element_add_class (element, "-x-evo-wrap-br"); - - max_length = length_to_wrap - line_length; - if (max_length < 0) - max_length = length_to_wrap; - else if (max_length == 0) { - if (check_next_node) { - insert_and_continue = TRUE; - goto check; - } - - /* Break before the current node and continue. */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - node, - NULL); - line_length = 0; - continue; - } - - /* Allow anchors to break on any character. */ - if (g_object_steal_data (G_OBJECT (node), "-x-evo-anchor-text")) - offset = max_length; - else { - /* Find where we can line-break the node so that it - * effectively fills the rest of current row. */ - offset = find_where_to_break_line ( - WEBKIT_DOM_CHARACTER_DATA (node), max_length); - - /* When pressing delete on the end of line to concatenate - * the last word from the line and first word from the - * next line we will end with the second word split - * somewhere in the middle (to be precise it will be - * split after the last character that will fit on the - * previous line. To avoid that we need to put the - * concatenated word on the next line. */ - if (offset == -1 || check_next_node) { - WebKitDOMNode *prev_sibling; - - check: - check_next_node = FALSE; - prev_sibling = webkit_dom_node_get_previous_sibling (node); - if (prev_sibling && e_html_editor_node_is_selection_position_node (prev_sibling)) { - WebKitDOMNode *prev_br = NULL; - - prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); - - /* Collapsed selection */ - if (prev_sibling && e_html_editor_node_is_selection_position_node (prev_sibling)) - prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); - - if (prev_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-wrap-br")) { - prev_br = prev_sibling; - prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); - } - - if (prev_sibling && WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling)) { - gchar *data; - glong text_length, length = 0; - - data = webkit_dom_character_data_get_data ( - WEBKIT_DOM_CHARACTER_DATA (prev_sibling)); - text_length = webkit_dom_character_data_get_length ( - WEBKIT_DOM_CHARACTER_DATA (prev_sibling)); - - /* Find the last character where we can break. */ - while (text_length - length > 0) { - if (strchr (" ", data[text_length - length - 1])) { - length++; - break; - } else if (data[text_length - length - 1] == '-' && - text_length - length > 1 && - !strchr (" ", data[text_length - length - 2])) - break; - length++; - } - - if (text_length != length) { - WebKitDOMNode *nd; - - webkit_dom_text_split_text ( - WEBKIT_DOM_TEXT (prev_sibling), - text_length - length, - NULL); - - if ((nd = webkit_dom_node_get_next_sibling (prev_sibling))) { - gchar *nd_content; - - nd_content = webkit_dom_node_get_text_content (nd); - if (nd_content && *nd_content) { - if (*nd_content == ' ') - mark_and_remove_leading_space (document, nd); - - if (!webkit_dom_node_get_next_sibling (nd) && - g_str_has_suffix (nd_content, " ")) - mark_and_remove_trailing_space (document, nd); - - g_free (nd_content); - } - - if (nd) { - if (prev_br) - remove_node (prev_br); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (nd), - WEBKIT_DOM_NODE (element), - nd, - NULL); - - offset = 0; - line_length = length; - continue; - } - } - } - } - } - if (insert_and_continue) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - node, - NULL); - - offset = 0; - line_length = 0; - insert_and_continue = FALSE; - continue; - } - - offset = offset != -1 ? offset : max_length; - } - } - - if (offset >= 0) { - WebKitDOMNode *nd; - - if (offset != length_left && offset != 0) { - webkit_dom_text_split_text ( - WEBKIT_DOM_TEXT (node), offset, NULL); - - nd = webkit_dom_node_get_next_sibling (node); - } else - nd = node; - - if (nd) { - gboolean no_sibling = FALSE; - gchar *nd_content; - - nd_content = webkit_dom_node_get_text_content (nd); - if (nd_content && *nd_content) { - if (*nd_content == ' ') - mark_and_remove_leading_space (document, nd); - - if (!webkit_dom_node_get_next_sibling (nd) && - length_left <= length_to_wrap && - g_str_has_suffix (nd_content, " ")) { - mark_and_remove_trailing_space (document, nd); - no_sibling = TRUE; - } - - g_free (nd_content); - } - - if (!no_sibling) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - nd, - NULL); - offset = 0; - - nd_content = webkit_dom_node_get_text_content (nd); - if (!*nd_content) - remove_node (nd); - g_free (nd_content); - - if (no_sibling) - node = NULL; - else - node = webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (element)); - } else { - webkit_dom_node_append_child ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - NULL); - } - } - if (node && WEBKIT_DOM_IS_CHARACTER_DATA (node)) - length_left = webkit_dom_character_data_get_length ( - WEBKIT_DOM_CHARACTER_DATA (node)); - - line_length = 0; - compensated = FALSE; - } - line_length += length_left - offset; - next_node: - if (!node) - break; - - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node)) { - line_length = 0; - compensated = FALSE; - } - - /* Move to next node */ - if (webkit_dom_node_has_child_nodes (node)) { - node = webkit_dom_node_get_first_child (node); - } else if (webkit_dom_node_get_next_sibling (node)) { - node = webkit_dom_node_get_next_sibling (node); - } else { - WebKitDOMNode *tmp_parent; - - if (webkit_dom_node_is_equal_node (node, start_node)) - break; - - /* Find a next node that we can process. */ - tmp_parent = webkit_dom_node_get_parent_node (node); - if (tmp_parent && webkit_dom_node_get_next_sibling (tmp_parent)) - node = webkit_dom_node_get_next_sibling (tmp_parent); - else { - WebKitDOMNode *tmp; - - tmp = tmp_parent; - /* Find a node that is not a start node (that would mean - * that we already processed the whole block) and it has - * a sibling that we can process. */ - while (tmp && !webkit_dom_node_is_equal_node (tmp, start_node) && - !webkit_dom_node_get_next_sibling (tmp)) { - tmp = webkit_dom_node_get_parent_node (tmp); - } - - /* If we found a node to process, let's process its - * sibling, otherwise give up. */ - if (tmp) - node = webkit_dom_node_get_next_sibling (tmp); - else - break; - } - } - } - - if (selection) { - gchar *html; - WebKitDOMElement *element; - - /* Create a wrapper DIV and put the processed content into it */ - element = webkit_dom_document_create_element (document, "DIV", NULL); - element_add_class (element, "-x-evo-paragraph"); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (start_node), - NULL); - - webkit_dom_node_normalize (WEBKIT_DOM_NODE (element)); - /* Get HTML code of the processed content */ - html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (element)); - - /* Overwrite the current selection by the processed content */ - e_html_editor_selection_insert_html (selection, html); - - g_free (html); - - return NULL; - } else { - WebKitDOMNode *last_child; - - last_child = webkit_dom_node_get_last_child (block_clone); - if (last_child && WEBKIT_DOM_IS_HTMLBR_ELEMENT (last_child) && - element_has_class (WEBKIT_DOM_ELEMENT (last_child), "-x-evo-wrap-br")) - remove_node (last_child); - - webkit_dom_node_normalize (block_clone); - - node = webkit_dom_node_get_parent_node (block); - if (node) { - /* Replace block with wrapped one */ - webkit_dom_node_replace_child ( - node, block_clone, block, NULL); - } - - return WEBKIT_DOM_ELEMENT (block_clone); - } -} - -void -e_html_editor_selection_set_indented_style (EHTMLEditorSelection *selection, - WebKitDOMElement *element, - gint width) -{ - gchar *style; - gint word_wrap_length; - - /* width < 0, set block width to word_wrap_length - * width == 0, no width limit set, - * width > 0, set width limit to given value */ - word_wrap_length = (width < 0) ? selection->priv->word_wrap_length : width; - - webkit_dom_element_set_class_name (element, "-x-evo-indented"); - - if (is_in_html_mode (selection) || word_wrap_length == 0) { - gchar *plain_text_style; - - style = g_strdup_printf ("margin-left: %dch;", SPACES_PER_INDENTATION); - - plain_text_style = g_strdup_printf ( - "margin-left: %dch; word-wrap: normal; width: %dch;", - SPACES_PER_INDENTATION, word_wrap_length); - - webkit_dom_element_set_attribute ( - element, "data-plain-text-style", plain_text_style, NULL); - g_free (plain_text_style); - } else { - style = g_strdup_printf ( - "margin-left: %dch; word-wrap: normal; width: %dch;", - SPACES_PER_INDENTATION, word_wrap_length); - } - - webkit_dom_element_set_attribute (element, "style", style, NULL); - g_free (style); -} - -WebKitDOMElement * -e_html_editor_selection_get_indented_element (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - gint width) -{ - WebKitDOMElement *element; - - element = webkit_dom_document_create_element (document, "DIV", NULL); - e_html_editor_selection_set_indented_style (selection, element, width); - - return element; -} - -void -e_html_editor_selection_set_paragraph_style (EHTMLEditorSelection *selection, - WebKitDOMElement *element, - gint width, - gint offset, - const gchar *style_to_add) -{ - char *style = NULL; - gint word_wrap_length = (width == -1) ? selection->priv->word_wrap_length : width; - WebKitDOMNode *parent; - - element_add_class (element, "-x-evo-paragraph"); - - /* Don't set the alignment for nodes as they are handled separately. */ - if (!node_is_list (WEBKIT_DOM_NODE (element))) { - EHTMLEditorSelectionAlignment alignment; - - alignment = e_html_editor_selection_get_alignment (selection); - element_add_class (element, get_css_alignment_value_class (alignment)); - } - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - /* Don't set the width limit to sub-blocks as the width limit is inhered - * from its parents. */ - if (!is_in_html_mode (selection) && - (!parent || WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent))) { - style = g_strdup_printf ( - "width: %dch; " - "word-wrap: break-word; " - "word-break: break-word; %s", - (word_wrap_length + offset), style_to_add); - } else { - if (*style_to_add) - style = g_strdup_printf ("%s", style_to_add); - } - if (style) { - webkit_dom_element_set_attribute (element, "style", style, NULL); - g_free (style); - } -} - -WebKitDOMElement * -e_html_editor_selection_get_paragraph_element (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - gint width, - gint offset) -{ - WebKitDOMElement *element; - - element = webkit_dom_document_create_element (document, "DIV", NULL); - e_html_editor_selection_set_paragraph_style (selection, element, width, offset, ""); - - return element; -} - -WebKitDOMElement * -e_html_editor_selection_put_node_into_paragraph (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - WebKitDOMNode *node, - gboolean with_input) -{ - WebKitDOMRange *range; - WebKitDOMElement *container; - - range = webkit_dom_document_create_range (document); - container = e_html_editor_selection_get_paragraph_element (selection, document, -1, 0); - webkit_dom_range_select_node (range, node, NULL); - webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (container), NULL); - /* We have to move caret position inside this container */ - if (with_input) - add_selection_markers_into_element_end (document, container, NULL, NULL); - - g_object_unref (range); - return container; -} - -/** - * e_html_editor_selection_wrap_lines: - * @selection: an #EHTMLEditorSelection - * - * Wraps all lines in current selection to be 71 characters long. - */ -void -e_html_editor_selection_wrap_lines (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - EHTMLEditorViewHistoryEvent *ev = NULL; - gboolean after_selection_end = FALSE, html_mode; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *block, *next_block; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - /* If the selection was not saved, move it into the first child of body */ - if (!selection_start_marker || !selection_end_marker) { - WebKitDOMHTMLElement *body; - WebKitDOMNode *child; - - body = webkit_dom_document_get_body (document); - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - add_selection_markers_into_element_start ( - document, - WEBKIT_DOM_ELEMENT (child), - &selection_start_marker, - &selection_end_marker); - } - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_WRAP; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - ev->data.style.from = 1; - ev->data.style.to = 1; - } - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - html_mode = e_html_editor_view_get_html_mode (view); - - /* Process all blocks that are in the selection one by one */ - while (block && !after_selection_end) { - gboolean quoted = FALSE; - gint citation_level, quote; - WebKitDOMElement *wrapped_paragraph; - - next_block = webkit_dom_node_get_next_sibling (block); - - /* Don't try to wrap the 'Normal' blocks as they are already wrapped and*/ - /* also skip blocks that we already wrapped with this function. */ - if ((!html_mode && element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-paragraph")) || - webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), "data-user-wrapped")) { - block = next_block; - continue; - } - - if (webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) { - quoted = TRUE; - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block)); - } - - if (!html_mode) - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block)); - - after_selection_end = webkit_dom_node_contains ( - block, WEBKIT_DOM_NODE (selection_end_marker)); - - citation_level = get_citation_level (block); - quote = citation_level ? citation_level * 2 : 0; - - wrapped_paragraph = e_html_editor_selection_wrap_paragraph_length ( - selection, WEBKIT_DOM_ELEMENT (block), selection->priv->word_wrap_length - quote); - - webkit_dom_element_set_attribute ( - wrapped_paragraph, "data-user-wrapped", "", NULL); - - if (quoted && !html_mode) - e_html_editor_view_quote_plain_text_element (view, wrapped_paragraph); - - block = next_block; - } - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_selection_restore (selection); - - e_html_editor_view_force_spell_check_in_viewport (view); - - g_object_unref (view); -} - -WebKitDOMElement * -e_html_editor_selection_wrap_paragraph_length (EHTMLEditorSelection *selection, - WebKitDOMElement *paragraph, - gint length) -{ - WebKitDOMDocument *document; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL); - g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL); - g_return_val_if_fail (length >= MINIMAL_PARAGRAPH_WIDTH, NULL); - - document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (paragraph)); - - return wrap_lines ( - NULL, WEBKIT_DOM_NODE (paragraph), document, FALSE, length, selection->priv->word_wrap_length); -} - -void -e_html_editor_selection_wrap_paragraphs_in_document (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - WebKitDOMNodeList *list; - gint ii, length; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - /* Only wrap paragraphs that are inside the quoted content, others are - * wrapped by CSS. */ - list = webkit_dom_document_query_selector_all ( - document, - "blockquote[type=cite] > div.-x-evo-paragraph:not(#-x-evo-input-start)", - NULL); - - length = webkit_dom_node_list_get_length (list); - - for (ii = 0; ii < length; ii++) { - gint quote, citation_level; - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - citation_level = get_citation_level (node); - quote = citation_level ? citation_level * 2 : 0; - - if (node_is_list (node)) { - WebKitDOMNode *item = webkit_dom_node_get_first_child (node); - - while (item && WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) { - e_html_editor_selection_wrap_paragraph_length ( - selection, - WEBKIT_DOM_ELEMENT (item), - selection->priv->word_wrap_length - quote); - item = webkit_dom_node_get_next_sibling (item); - } - } else { - e_html_editor_selection_wrap_paragraph_length ( - selection, - WEBKIT_DOM_ELEMENT (node), - selection->priv->word_wrap_length - quote); - } - g_object_unref (node); - } - g_object_unref (list); -} - -WebKitDOMElement * -e_html_editor_selection_wrap_paragraph (EHTMLEditorSelection *selection, - WebKitDOMElement *paragraph) -{ - gint indentation_level, citation_level, quote; - gint final_width, word_wrap_length, offset = 0; - - g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL); - g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL); - - word_wrap_length = selection->priv->word_wrap_length; - indentation_level = get_indentation_level (paragraph); - citation_level = get_citation_level (WEBKIT_DOM_NODE (paragraph)); - - if (node_is_list_or_item (WEBKIT_DOM_NODE (paragraph))) { - gint list_level = get_list_level (WEBKIT_DOM_NODE (paragraph)); - indentation_level = 0; - - if (list_level > 0) - offset = list_level * -SPACES_PER_LIST_LEVEL; - else - offset = -SPACES_PER_LIST_LEVEL; - } - - quote = citation_level ? citation_level * 2 : 0; - - final_width = word_wrap_length - quote + offset; - final_width -= SPACES_PER_INDENTATION * indentation_level; - - return e_html_editor_selection_wrap_paragraph_length ( - selection, WEBKIT_DOM_ELEMENT (paragraph), final_width); -} - -static WebKitDOMNode * -in_empty_block_in_quoted_content (WebKitDOMNode *element) -{ - WebKitDOMNode *first_child, *next_sibling; - - first_child = webkit_dom_node_get_first_child (element); - if (!WEBKIT_DOM_IS_ELEMENT (first_child)) - return NULL; - - if (!element_has_class (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-quoted")) - return NULL; - - next_sibling = webkit_dom_node_get_next_sibling (first_child); - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) - return next_sibling; - - if (!WEBKIT_DOM_IS_ELEMENT (next_sibling)) - return NULL; - - if (!element_has_id (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-selection-start-marker")) - return NULL; - - next_sibling = webkit_dom_node_get_next_sibling (next_sibling); - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) - return next_sibling; - - return NULL; -} - -/** - * e_html_editor_selection_save: - * @selection: an #EHTMLEditorSelection - * - * Saves current cursor position or current selection range. The selection can - * be later restored by calling e_html_editor_selection_restore(). - * - * Note that calling e_html_editor_selection_save() overwrites previously saved - * position. - * - * Note that this method inserts special markings into the HTML code that are - * used to later restore the selection. It can happen that by deleting some - * segments of the document some of the markings are deleted too. In that case - * restoring the selection by e_html_editor_selection_restore() can fail. Also by - * moving text segments (Cut & Paste) can result in moving the markings - * elsewhere, thus e_html_editor_selection_restore() will restore the selection - * incorrectly. - * - * It is recommended to use this method only when you are not planning to make - * bigger changes to content or structure of the document (formatting changes - * are usually OK). - */ -void -e_html_editor_selection_save (EHTMLEditorSelection *selection) -{ - gboolean collapsed = FALSE; - glong offset, anchor_offset; - EHTMLEditorView *view; - WebKitWebView *web_view; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - WebKitDOMNode *container, *next_sibling, *marker_node; - WebKitDOMNode *split_node, *parent_node, *anchor; - WebKitDOMElement *start_marker = NULL, *end_marker = NULL; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - web_view = WEBKIT_WEB_VIEW (view); - - document = webkit_web_view_get_dom_document (web_view); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - g_object_unref (view); - - /* First remove all markers (if present) */ - remove_selection_markers (document); - - if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { - g_object_unref (dom_selection); - g_object_unref (dom_window); - return; - } - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - if (!range) { - g_object_unref (dom_selection); - g_object_unref (dom_window); - return; - } - - anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection); - anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection); - - collapsed = webkit_dom_range_get_collapsed (range, NULL); - start_marker = create_selection_marker (document, TRUE); - - container = webkit_dom_range_get_start_container (range, NULL); - offset = webkit_dom_range_get_start_offset (range, NULL); - parent_node = webkit_dom_node_get_parent_node (container); - - if (webkit_dom_node_is_same_node (anchor, container) && offset == anchor_offset) - webkit_dom_element_set_attribute (start_marker, "data-anchor", "", NULL); - - if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-quote-character")) { - WebKitDOMNode *node; - - node = webkit_dom_node_get_parent_node ( - webkit_dom_node_get_parent_node (parent_node)); - - if ((next_sibling = in_empty_block_in_quoted_content (node))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (next_sibling), - WEBKIT_DOM_NODE (start_marker), - next_sibling, - NULL); - } else { - webkit_dom_node_insert_before ( - node, - WEBKIT_DOM_NODE (start_marker), - webkit_dom_node_get_next_sibling ( - webkit_dom_node_get_parent_node (parent_node)), - NULL); - } - goto insert_end_marker; - } else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-smiley-text")) { - WebKitDOMNode *node; - - node = webkit_dom_node_get_parent_node (parent_node); - if (offset == 0) { - marker_node = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (start_marker), - node, - NULL); - goto insert_end_marker; - } - } else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "Apple-tab-span") && offset == 1) { - marker_node = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent_node), - WEBKIT_DOM_NODE (start_marker), - webkit_dom_node_get_next_sibling (parent_node), - NULL); - goto insert_end_marker; - } - - if (WEBKIT_DOM_IS_TEXT (container)) { - if (offset != 0) { - WebKitDOMText *split_text; - - split_text = webkit_dom_text_split_text ( - WEBKIT_DOM_TEXT (container), offset, NULL); - split_node = WEBKIT_DOM_NODE (split_text); - } else { - marker_node = webkit_dom_node_insert_before ( - parent_node, - WEBKIT_DOM_NODE (start_marker), - container, - NULL); - goto insert_end_marker; - } - } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (container)) { - marker_node = webkit_dom_node_insert_before ( - container, - WEBKIT_DOM_NODE (start_marker), - webkit_dom_node_get_first_child (container), - NULL); - goto insert_end_marker; - } else if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (container)) { - marker_node = webkit_dom_node_insert_before ( - container, - WEBKIT_DOM_NODE (start_marker), - webkit_dom_node_get_first_child (container), - NULL); - goto insert_end_marker; - } else { - /* Insert the selection marker on the right position in - * an empty paragraph in the quoted content */ - if ((next_sibling = in_empty_block_in_quoted_content (container))) { - marker_node = webkit_dom_node_insert_before ( - container, - WEBKIT_DOM_NODE (start_marker), - next_sibling, - NULL); - goto insert_end_marker; - } - if (!webkit_dom_node_get_previous_sibling (container)) { - marker_node = webkit_dom_node_insert_before ( - container, - WEBKIT_DOM_NODE (start_marker), - webkit_dom_node_get_first_child (container), - NULL); - goto insert_end_marker; - } else if (!webkit_dom_node_get_next_sibling (container)) { - WebKitDOMNode *tmp; - - tmp = webkit_dom_node_get_last_child (container); - if (tmp && WEBKIT_DOM_IS_HTMLBR_ELEMENT (tmp)) - marker_node = webkit_dom_node_insert_before ( - container, - WEBKIT_DOM_NODE (start_marker), - tmp, - NULL); - else - marker_node = webkit_dom_node_append_child ( - container, - WEBKIT_DOM_NODE (start_marker), - NULL); - goto insert_end_marker; - } else { - if (webkit_dom_node_get_first_child (container)) { - marker_node = webkit_dom_node_insert_before ( - container, - WEBKIT_DOM_NODE (start_marker), - webkit_dom_node_get_first_child (container), - NULL); - goto insert_end_marker; - } - split_node = container; - } - } - - /* Don't save selection straight into body */ - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (split_node)) - goto out; - - if (!split_node) { - marker_node = webkit_dom_node_insert_before ( - container, - WEBKIT_DOM_NODE (start_marker), - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (container)), - NULL); - } else { - marker_node = WEBKIT_DOM_NODE (start_marker); - parent_node = webkit_dom_node_get_parent_node (split_node); - - webkit_dom_node_insert_before ( - parent_node, marker_node, split_node, NULL); - } - - webkit_dom_node_normalize (parent_node); - - insert_end_marker: - end_marker = create_selection_marker (document, FALSE); - - if (collapsed) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (start_marker)), - WEBKIT_DOM_NODE (end_marker), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_marker)), - NULL); - goto out; - } - - container = webkit_dom_range_get_end_container (range, NULL); - offset = webkit_dom_range_get_end_offset (range, NULL); - parent_node = webkit_dom_node_get_parent_node (container); - - if (webkit_dom_node_is_same_node (anchor, container) && offset == anchor_offset) - webkit_dom_element_set_attribute (end_marker, "data-anchor", "", NULL); - - if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-quote-character")) { - WebKitDOMNode *node; - - node = webkit_dom_node_get_parent_node ( - webkit_dom_node_get_parent_node (parent_node)); - - if ((next_sibling = in_empty_block_in_quoted_content (node))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (next_sibling), - WEBKIT_DOM_NODE (end_marker), - next_sibling, - NULL); - } else { - webkit_dom_node_insert_before ( - node, - WEBKIT_DOM_NODE (end_marker), - webkit_dom_node_get_next_sibling ( - webkit_dom_node_get_parent_node (parent_node)), - NULL); - } - goto out; - } - - if (WEBKIT_DOM_IS_TEXT (container)) { - if (offset != 0) { - WebKitDOMText *split_text; - - split_text = webkit_dom_text_split_text ( - WEBKIT_DOM_TEXT (container), offset, NULL); - split_node = WEBKIT_DOM_NODE (split_text); - } else { - marker_node = webkit_dom_node_insert_before ( - parent_node, WEBKIT_DOM_NODE (end_marker), container, NULL); - goto check; - - } - } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (container)) { - webkit_dom_node_append_child ( - container, WEBKIT_DOM_NODE (end_marker), NULL); - goto out; - } else { - /* Insert the selection marker on the right position in - * an empty paragraph in the quoted content */ - if ((next_sibling = in_empty_block_in_quoted_content (container))) { - webkit_dom_node_insert_before ( - container, - WEBKIT_DOM_NODE (end_marker), - next_sibling, - NULL); - goto out; - } - if (!webkit_dom_node_get_previous_sibling (container)) { - split_node = parent_node; - } else if (!webkit_dom_node_get_next_sibling (container) && - !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) { - split_node = parent_node; - split_node = webkit_dom_node_get_next_sibling (split_node); - } else - split_node = container; - } - - /* Don't save selection straight into body */ - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (split_node)) { - remove_node (WEBKIT_DOM_NODE (start_marker)); - g_object_unref (range); - g_object_unref (dom_selection); - g_object_unref (dom_window); - return; - } - - marker_node = WEBKIT_DOM_NODE (end_marker); - - if (split_node) { - parent_node = webkit_dom_node_get_parent_node (split_node); - - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) { - if (offset == 0) - webkit_dom_node_insert_before ( - split_node, - marker_node, - webkit_dom_node_get_first_child (split_node), - NULL); - else - webkit_dom_node_append_child ( - webkit_dom_node_get_previous_sibling (split_node), - marker_node, - NULL); - } else - webkit_dom_node_insert_before ( - parent_node, marker_node, split_node, NULL); - } else { - WebKitDOMNode *first_child; - - first_child = webkit_dom_node_get_first_child (container); - if (offset == 0 && WEBKIT_DOM_IS_TEXT (first_child)) - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (container), marker_node, webkit_dom_node_get_first_child (container), NULL); - else - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (container), marker_node, NULL); - } - - webkit_dom_node_normalize (parent_node); - - check: - if ((next_sibling = webkit_dom_node_get_next_sibling (marker_node))) { - if (!WEBKIT_DOM_IS_ELEMENT (next_sibling)) - next_sibling = webkit_dom_node_get_next_sibling (next_sibling); - /* If the selection is collapsed ensure that the selection start marker - * is before the end marker */ - if (next_sibling && webkit_dom_node_is_same_node (next_sibling, WEBKIT_DOM_NODE (start_marker))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (marker_node), - next_sibling, - marker_node, - NULL); - } - } - out: - if (!collapsed) { - if (start_marker && end_marker) { - webkit_dom_range_set_start_after (range, WEBKIT_DOM_NODE (start_marker), NULL); - webkit_dom_range_set_end_before (range, WEBKIT_DOM_NODE (end_marker), NULL); - } else { - g_warn_if_reached (); - } - - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - } - - g_object_unref (range); - g_object_unref (dom_selection); - g_object_unref (dom_window); -} - -/** - * e_html_editor_selection_restore: - * @selection: an #EHTMLEditorSelection - * - * Restores cursor position or selection range that was saved by - * e_html_editor_selection_save(). - * - * Note that calling this function without calling e_html_editor_selection_save() - * before is a programming error and the behavior is undefined. - */ -void -e_html_editor_selection_restore (EHTMLEditorSelection *selection) -{ - EHTMLEditorView *view; - gboolean start_is_anchor = FALSE; - glong offset; - WebKitWebView *web_view; - WebKitDOMDocument *document; - WebKitDOMElement *marker; - WebKitDOMNode *selection_start_marker, *selection_end_marker; - WebKitDOMNode *parent_start, *parent_end, *anchor; - WebKitDOMRange *range; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMDOMWindow *dom_window; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - web_view = WEBKIT_WEB_VIEW (view); - - document = webkit_web_view_get_dom_document (web_view); - g_object_unref (view); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - g_object_unref (dom_window); - if (!range) { - WebKitDOMHTMLElement *body; - - range = webkit_dom_document_create_range (document); - body = webkit_dom_document_get_body (document); - - webkit_dom_range_select_node_contents (range, WEBKIT_DOM_NODE (body), NULL); - webkit_dom_range_collapse (range, TRUE, NULL); - webkit_dom_dom_selection_add_range (dom_selection, range); - } - - selection_start_marker = webkit_dom_range_get_start_container (range, NULL); - if (selection_start_marker) { - gboolean ok = FALSE; - selection_start_marker = - webkit_dom_node_get_next_sibling (selection_start_marker); - - ok = e_html_editor_node_is_selection_position_node (selection_start_marker); - - if (ok) { - ok = FALSE; - if (webkit_dom_range_get_collapsed (range, NULL)) { - selection_end_marker = webkit_dom_node_get_next_sibling ( - selection_start_marker); - - ok = e_html_editor_node_is_selection_position_node (selection_end_marker); - if (ok) { - WebKitDOMNode *next_sibling; - - next_sibling = webkit_dom_node_get_next_sibling (selection_end_marker); - - if (next_sibling && !WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) { - parent_start = webkit_dom_node_get_parent_node (selection_end_marker); - - remove_node (selection_start_marker); - remove_node (selection_end_marker); - - webkit_dom_node_normalize (parent_start); - g_object_unref (range); - g_object_unref (dom_selection); - return; - } - } - } - } - } - - g_object_unref (range); - range = webkit_dom_document_create_range (document); - if (!range) { - g_object_unref (dom_selection); - return; - } - - marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (!marker) { - marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - if (marker) - remove_node (WEBKIT_DOM_NODE (marker)); - g_object_unref (dom_selection); - g_object_unref (range); - return; - } - - start_is_anchor = webkit_dom_element_has_attribute (marker, "data-anchor"); - parent_start = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker)); - - webkit_dom_range_set_start_after (range, WEBKIT_DOM_NODE (marker), NULL); - remove_node (WEBKIT_DOM_NODE (marker)); - - marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - if (!marker) { - marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (marker) - remove_node (WEBKIT_DOM_NODE (marker)); - g_object_unref (dom_selection); - g_object_unref (range); - return; - } - - parent_end = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker)); - webkit_dom_range_set_end_before (range, WEBKIT_DOM_NODE (marker), NULL); - remove_node (WEBKIT_DOM_NODE (marker)); - - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - if (webkit_dom_node_is_same_node (parent_start, parent_end)) - webkit_dom_node_normalize (parent_start); - else { - webkit_dom_node_normalize (parent_start); - webkit_dom_node_normalize (parent_end); - } - - if (start_is_anchor) { - anchor = webkit_dom_range_get_end_container (range, NULL); - offset = webkit_dom_range_get_end_offset (range, NULL); - - webkit_dom_range_collapse (range, TRUE, NULL); - } else { - anchor = webkit_dom_range_get_start_container (range, NULL); - offset = webkit_dom_range_get_start_offset (range, NULL); - - webkit_dom_range_collapse (range, FALSE, NULL); - } - webkit_dom_dom_selection_add_range (dom_selection, range); - webkit_dom_dom_selection_extend (dom_selection, anchor, offset, NULL); - g_object_unref (range); - g_object_unref (dom_selection); -} - -void -e_html_editor_selection_set_on_point (EHTMLEditorSelection *selection, - guint x, - guint y) -{ - EHTMLEditorView *view; - WebKitDOMRange *range; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - g_object_unref (view); - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - range = webkit_dom_document_caret_range_from_point (document, x, y); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - g_object_unref (dom_selection); - g_object_unref (dom_window); -} - -static void -html_editor_selection_modify (EHTMLEditorSelection *selection, - const gchar *alter, - gboolean forward, - EHTMLEditorSelectionGranularity granularity) -{ - EHTMLEditorView *view; - WebKitWebView *web_view; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - const gchar *granularity_str = NULL; - - g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection)); - - view = e_html_editor_selection_ref_html_editor_view (selection); - g_return_if_fail (view != NULL); - - web_view = WEBKIT_WEB_VIEW (view); - - document = webkit_web_view_get_dom_document (web_view); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - switch (granularity) { - case E_HTML_EDITOR_SELECTION_GRANULARITY_CHARACTER: - granularity_str = "character"; - break; - case E_HTML_EDITOR_SELECTION_GRANULARITY_WORD: - granularity_str = "word"; - break; - } - - if (granularity_str) { - webkit_dom_dom_selection_modify ( - dom_selection, alter, - forward ? "forward" : "backward", - granularity_str); - } - - g_object_unref (dom_selection); - g_object_unref (dom_window); - g_object_unref (view); -} - -/** - * e_html_editor_selection_extend: - * @selection: an #EHTMLEditorSelection - * @forward: whether to extend selection forward or backward - * @granularity: granularity of the extension - * - * Extends current selection in given direction by given granularity. - */ -void -e_html_editor_selection_extend (EHTMLEditorSelection *selection, - gboolean forward, - EHTMLEditorSelectionGranularity granularity) -{ - html_editor_selection_modify (selection, "extend", forward, granularity); -} - -/** - * e_html_editor_selection_move: - * @selection: an #EHTMLEditorSelection - * @forward: whether to move the selection forward or backward - * @granularity: granularity of the movement - * - * Moves current selection in given direction by given granularity - */ -void -e_html_editor_selection_move (EHTMLEditorSelection *selection, - gboolean forward, - EHTMLEditorSelectionGranularity granularity) -{ - html_editor_selection_modify (selection, "move", forward, granularity); -} - -void -e_html_editor_selection_scroll_to_caret (EHTMLEditorSelection *selection) -{ - glong element_top, element_left; - glong window_top, window_left, window_right, window_bottom; - EHTMLEditorView *view; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMElement *selection_start_marker; - - e_html_editor_selection_save (selection); - - view = e_html_editor_selection_ref_html_editor_view (selection); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - g_object_unref (view); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (!selection_start_marker) - return; - - dom_window = webkit_dom_document_get_default_view (document); - - window_top = webkit_dom_dom_window_get_scroll_y (dom_window); - window_left = webkit_dom_dom_window_get_scroll_x (dom_window); - window_bottom = window_top + webkit_dom_dom_window_get_inner_height (dom_window); - window_right = window_left + webkit_dom_dom_window_get_inner_width (dom_window); - - element_left = webkit_dom_element_get_offset_left (selection_start_marker); - element_top = webkit_dom_element_get_offset_top (selection_start_marker); - - /* Check if caret is inside viewport, if not move to it */ - if (!(element_top >= window_top && element_top <= window_bottom && - element_left >= window_left && element_left <= window_right)) { - webkit_dom_element_scroll_into_view (selection_start_marker, TRUE); - } - - e_html_editor_selection_restore (selection); - - g_object_unref (dom_window); -} diff --git a/e-util/e-html-editor-selection.h b/e-util/e-html-editor-selection.h deleted file mode 100644 index b1028e2..0000000 --- a/e-util/e-html-editor-selection.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * e-html-editor-selection.h - * - * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com> - * - * 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 <http://www.gnu.org/licenses/> - * - */ - -#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION) -#error "Only <e-util/e-util.h> should be included directly." -#endif - -#ifndef E_HTML_EDITOR_SELECTION_H -#define E_HTML_EDITOR_SELECTION_H - -#include <gtk/gtk.h> -#include <e-util/e-util-enums.h> -#include <webkit/webkit.h> - -/* Standard GObject macros */ -#define E_TYPE_HTML_EDITOR_SELECTION \ - (e_html_editor_selection_get_type ()) -#define E_HTML_EDITOR_SELECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelection)) -#define E_HTML_EDITOR_SELECTION_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionClass)) -#define E_IS_HTML_EDITOR_SELECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_HTML_EDITOR_SELECTION)) -#define E_IS_HTML_EDITOR_SELECTION_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_HTML_EDITOR_SELECTION)) -#define E_HTML_EDITOR_SELECTION_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionClass)) - -G_BEGIN_DECLS - -struct _EHTMLEditorView; - -typedef struct _EHTMLEditorSelection EHTMLEditorSelection; -typedef struct _EHTMLEditorSelectionClass EHTMLEditorSelectionClass; -typedef struct _EHTMLEditorSelectionPrivate EHTMLEditorSelectionPrivate; - -struct _EHTMLEditorSelection { - GObject parent; - EHTMLEditorSelectionPrivate *priv; -}; - -struct _EHTMLEditorSelectionClass { - GObjectClass parent_class; -}; - -GType e_html_editor_selection_get_type - (void) G_GNUC_CONST; -struct _EHTMLEditorView * - e_html_editor_selection_ref_html_editor_view - (EHTMLEditorSelection *selection); -void e_html_editor_selection_block_selection_changed - (EHTMLEditorSelection *selection); -void e_html_editor_selection_unblock_selection_changed - (EHTMLEditorSelection *selection); -gint e_html_editor_selection_get_word_wrap_length - (EHTMLEditorSelection *selection); -gboolean e_html_editor_selection_has_text - (EHTMLEditorSelection *selection); -gchar * e_html_editor_selection_get_caret_word - (EHTMLEditorSelection *selection); -void e_html_editor_selection_replace_caret_word - (EHTMLEditorSelection *selection, - const gchar *replacement); -EHTMLEditorSelectionAlignment - e_html_editor_selection_get_alignment - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_alignment - (EHTMLEditorSelection *selection, - EHTMLEditorSelectionAlignment alignment); -const gchar * e_html_editor_selection_get_background_color - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_background_color - (EHTMLEditorSelection *selection, - const gchar *color); -void e_html_editor_selection_get_font_color - (EHTMLEditorSelection *selection, - GdkRGBA *rgba); -void e_html_editor_selection_set_font_color - (EHTMLEditorSelection *selection, - const GdkRGBA *rgba); -const gchar * e_html_editor_selection_get_font_name - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_font_name - (EHTMLEditorSelection *selection, - const gchar *font_name); -guint e_html_editor_selection_get_font_size - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_font_size - (EHTMLEditorSelection *selection, - guint font_size); -EHTMLEditorSelectionBlockFormat - e_html_editor_selection_get_block_format - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_block_format - (EHTMLEditorSelection *selection, - EHTMLEditorSelectionBlockFormat format); -gboolean e_html_editor_selection_is_citation - (EHTMLEditorSelection *selection); -gboolean e_html_editor_selection_is_indented - (EHTMLEditorSelection *selection); -void e_html_editor_selection_indent (EHTMLEditorSelection *selection); -void e_html_editor_selection_unindent - (EHTMLEditorSelection *selection); -gboolean e_html_editor_selection_is_bold (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_bold - (EHTMLEditorSelection *selection, - gboolean bold); -gboolean e_html_editor_selection_is_italic - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_italic - (EHTMLEditorSelection *selection, - gboolean italic); -gboolean e_html_editor_selection_is_monospaced - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_monospaced - (EHTMLEditorSelection *selection, - gboolean monospaced); -gboolean e_html_editor_selection_is_strikethrough - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_strikethrough - (EHTMLEditorSelection *selection, - gboolean strikethrough); -gboolean e_html_editor_selection_is_superscript - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_superscript - (EHTMLEditorSelection *selection, - gboolean superscript); -gboolean e_html_editor_selection_is_subscript - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_subscript - (EHTMLEditorSelection *selection, - gboolean subscript); -gboolean e_html_editor_selection_is_underline - (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_underline - (EHTMLEditorSelection *selection, - gboolean underline); -void e_html_editor_selection_unlink (EHTMLEditorSelection *selection); -void e_html_editor_selection_create_link - (EHTMLEditorSelection *selection, - const gchar *uri); -gboolean e_html_editor_selection_is_collapsed - (EHTMLEditorSelection *selection); -const gchar * e_html_editor_selection_get_string - (EHTMLEditorSelection *selection); -void e_html_editor_selection_replace (EHTMLEditorSelection *selection, - const gchar *new_string); -void e_html_editor_selection_insert_text - (EHTMLEditorSelection *selection, - const gchar *plain_text); -void e_html_editor_selection_insert_html - (EHTMLEditorSelection *selection, - const gchar *html_text); -void e_html_editor_selection_insert_as_text - (EHTMLEditorSelection *selection, - const gchar *html_text); -void e_html_editor_selection_replace_image_src - (EHTMLEditorSelection *selection, - WebKitDOMElement *element, - const gchar *image_uri); -void e_html_editor_selection_insert_image - (EHTMLEditorSelection *selection, - const gchar *image_uri); -void e_html_editor_selection_move_caret_into_element - (WebKitDOMDocument *document, - WebKitDOMElement *element, - gboolean to_start); -void e_html_editor_selection_set_indented_style - (EHTMLEditorSelection *selection, - WebKitDOMElement *element, - gint width); -WebKitDOMElement * - e_html_editor_selection_get_indented_element - (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - gint width); -void e_html_editor_selection_set_paragraph_style - (EHTMLEditorSelection *selection, - WebKitDOMElement *element, - gint width, - gint offset, - const gchar *style_to_add); -WebKitDOMElement * - e_html_editor_selection_get_paragraph_element - (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - gint width, - gint offset); -WebKitDOMElement * - e_html_editor_selection_put_node_into_paragraph - (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - WebKitDOMNode *node, - gboolean with_input); -void e_html_editor_selection_wrap_lines - (EHTMLEditorSelection *selection); -WebKitDOMElement * - e_html_editor_selection_wrap_paragraph_length - (EHTMLEditorSelection *selection, - WebKitDOMElement *paragraph, - gint length); -void e_html_editor_selection_wrap_paragraphs_in_document - (EHTMLEditorSelection *selection, - WebKitDOMDocument *document); -WebKitDOMElement * - e_html_editor_selection_wrap_paragraph - (EHTMLEditorSelection *selection, - WebKitDOMElement *paragraph); -void e_html_editor_selection_save (EHTMLEditorSelection *selection); -void e_html_editor_selection_restore (EHTMLEditorSelection *selection); -void e_html_editor_selection_set_on_point - (EHTMLEditorSelection *selection, - guint x, - guint y); -void e_html_editor_selection_move (EHTMLEditorSelection *selection, - gboolean forward, - EHTMLEditorSelectionGranularity granularity); -void e_html_editor_selection_extend (EHTMLEditorSelection *selection, - gboolean forward, - EHTMLEditorSelectionGranularity granularity); -void e_html_editor_selection_scroll_to_caret - (EHTMLEditorSelection *selection); -EHTMLEditorSelectionAlignment - e_html_editor_selection_get_list_alignment_from_node - (WebKitDOMNode *node); -void remove_wrapping_from_element (WebKitDOMElement *element); -void remove_quoting_from_element (WebKitDOMElement *element); -void e_html_editor_selection_get_selection_coordinates - (EHTMLEditorSelection *selection, - guint *start_x, - guint *start_y, - guint *end_x, - guint *end_y); -G_END_DECLS - -#endif /* E_HTML_EDITOR_SELECTION_H */ diff --git a/e-util/e-html-editor-spell-check-dialog.c b/e-util/e-html-editor-spell-check-dialog.c index 5d83ec2..d7447e0 100644 --- a/e-util/e-html-editor-spell-check-dialog.c +++ b/e-util/e-html-editor-spell-check-dialog.c @@ -27,7 +27,6 @@ #include <glib/gi18n-lib.h> #include <enchant/enchant.h> -#include "e-html-editor-view.h" #include "e-spell-checker.h" #include "e-spell-dictionary.h" @@ -48,8 +47,6 @@ struct _EHTMLEditorSpellCheckDialogPrivate { GtkWidget *suggestion_label; GtkWidget *tree_view; - WebKitDOMDOMSelection *selection; - gchar *word; ESpellDictionary *current_dict; }; @@ -70,7 +67,7 @@ html_editor_spell_check_dialog_set_word (EHTMLEditorSpellCheckDialog *dialog, const gchar *word) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GtkTreeView *tree_view; GtkListStore *store; gchar *markup; @@ -127,97 +124,27 @@ html_editor_spell_check_dialog_set_word (EHTMLEditorSpellCheckDialog *dialog, * given to WebKit, because this dialog is modal, but it satisfies * it in a way that it paints the selection :) */ editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - gtk_widget_grab_focus (GTK_WIDGET (view)); -} - -static gboolean -select_next_word (EHTMLEditorSpellCheckDialog *dialog) -{ - WebKitDOMNode *anchor, *focus; - gulong anchor_offset, focus_offset; - - anchor = webkit_dom_dom_selection_get_anchor_node (dialog->priv->selection); - anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dialog->priv->selection); - - focus = webkit_dom_dom_selection_get_focus_node (dialog->priv->selection); - focus_offset = webkit_dom_dom_selection_get_focus_offset (dialog->priv->selection); - - /* Jump _behind_ next word */ - webkit_dom_dom_selection_modify ( - dialog->priv->selection, "move", "forward", "word"); - /* Jump before the word */ - webkit_dom_dom_selection_modify ( - dialog->priv->selection, "move", "backward", "word"); - /* Select it */ - webkit_dom_dom_selection_modify ( - dialog->priv->selection, "extend", "forward", "word"); - - /* If the selection didn't change, then we have most probably - * reached the end of document - return FALSE */ - return !((anchor == webkit_dom_dom_selection_get_anchor_node ( - dialog->priv->selection)) && - (anchor_offset == webkit_dom_dom_selection_get_anchor_offset ( - dialog->priv->selection)) && - (focus == webkit_dom_dom_selection_get_focus_node ( - dialog->priv->selection)) && - (focus_offset == webkit_dom_dom_selection_get_focus_offset ( - dialog->priv->selection))); + cnt_editor = e_html_editor_get_content_editor (editor); + gtk_widget_grab_focus (GTK_WIDGET (cnt_editor)); } static gboolean html_editor_spell_check_dialog_next (EHTMLEditorSpellCheckDialog *dialog) { - WebKitDOMNode *start = NULL, *end = NULL; - gulong start_offset = 0, end_offset = 0; - - if (dialog->priv->word == NULL) { - webkit_dom_dom_selection_modify ( - dialog->priv->selection, "move", "left", "documentboundary"); - } else { - /* Remember last selected word */ - start = webkit_dom_dom_selection_get_anchor_node ( - dialog->priv->selection); - end = webkit_dom_dom_selection_get_focus_node ( - dialog->priv->selection); - start_offset = webkit_dom_dom_selection_get_anchor_offset ( - dialog->priv->selection); - end_offset = webkit_dom_dom_selection_get_focus_offset ( - dialog->priv->selection); - } + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gchar *next_word; - while (select_next_word (dialog)) { - WebKitDOMRange *range; - WebKitSpellChecker *checker; - gint loc, len; - gchar *word; - - range = webkit_dom_dom_selection_get_range_at ( - dialog->priv->selection, 0, NULL); - word = webkit_dom_range_get_text (range); - g_object_unref (range); - - checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ()); - webkit_spell_checker_check_spelling_of_string ( - checker, word, &loc, &len); - - /* Found misspelled word! */ - if (loc != -1) { - html_editor_spell_check_dialog_set_word (dialog, word); - g_free (word); - return TRUE; - } - - g_free (word); - } + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - /* Restore the selection to contain the last misspelled word. This is - * reached only when we reach the end of the document */ - if (start && end) { - webkit_dom_dom_selection_set_base_and_extent ( - dialog->priv->selection, start, start_offset, - end, end_offset, NULL); + next_word = e_content_editor_spell_check_next_word (cnt_editor, dialog->priv->word); + if (next_word && *next_word) { + html_editor_spell_check_dialog_set_word (dialog, next_word); + g_free (next_word); + return TRUE; } + g_free (next_word); /* Close the dialog */ gtk_widget_hide (GTK_WIDGET (dialog)); @@ -225,117 +152,53 @@ html_editor_spell_check_dialog_next (EHTMLEditorSpellCheckDialog *dialog) } static gboolean -select_previous_word (EHTMLEditorSpellCheckDialog *dialog) -{ - WebKitDOMNode *old_anchor_node; - WebKitDOMNode *new_anchor_node; - gulong old_anchor_offset; - gulong new_anchor_offset; - - old_anchor_node = webkit_dom_dom_selection_get_anchor_node ( - dialog->priv->selection); - old_anchor_offset = webkit_dom_dom_selection_get_anchor_offset ( - dialog->priv->selection); - - /* Jump on the beginning of current word */ - webkit_dom_dom_selection_modify ( - dialog->priv->selection, "move", "backward", "word"); - /* Jump before previous word */ - webkit_dom_dom_selection_modify ( - dialog->priv->selection, "move", "backward", "word"); - /* Select it */ - webkit_dom_dom_selection_modify ( - dialog->priv->selection, "extend", "forward", "word"); - - /* If the selection start didn't change, then we have most probably - * reached the beginnig of document. Return FALSE */ - - new_anchor_node = webkit_dom_dom_selection_get_anchor_node ( - dialog->priv->selection); - new_anchor_offset = webkit_dom_dom_selection_get_anchor_offset ( - dialog->priv->selection); - - return (new_anchor_node != old_anchor_node) || - (new_anchor_offset != old_anchor_offset); -} - -static gboolean html_editor_spell_check_dialog_prev (EHTMLEditorSpellCheckDialog *dialog) { - WebKitDOMNode *start = NULL, *end = NULL; - gulong start_offset = 0, end_offset = 0; - - if (dialog->priv->word == NULL) { - webkit_dom_dom_selection_modify ( - dialog->priv->selection, - "move", "right", "documentboundary"); - webkit_dom_dom_selection_modify ( - dialog->priv->selection, - "extend", "backward", "word"); - } else { - /* Remember last selected word */ - start = webkit_dom_dom_selection_get_anchor_node ( - dialog->priv->selection); - end = webkit_dom_dom_selection_get_focus_node ( - dialog->priv->selection); - start_offset = webkit_dom_dom_selection_get_anchor_offset ( - dialog->priv->selection); - end_offset = webkit_dom_dom_selection_get_focus_offset ( - dialog->priv->selection); - } + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gchar *prev_word; - while (select_previous_word (dialog)) { - WebKitDOMRange *range; - WebKitSpellChecker *checker; - gint loc, len; - gchar *word; - - range = webkit_dom_dom_selection_get_range_at ( - dialog->priv->selection, 0, NULL); - word = webkit_dom_range_get_text (range); - g_object_unref (range); - - checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ()); - webkit_spell_checker_check_spelling_of_string ( - checker, word, &loc, &len); - - /* Found misspelled word! */ - if (loc != -1) { - html_editor_spell_check_dialog_set_word (dialog, word); - g_free (word); - return TRUE; - } - - g_free (word); - } + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - /* Restore the selection to contain the last misspelled word. This is - * reached only when we reach the beginning of the document */ - if (start && end) { - webkit_dom_dom_selection_set_base_and_extent ( - dialog->priv->selection, start, start_offset, - end, end_offset, NULL); + prev_word = e_content_editor_spell_check_prev_word (cnt_editor, dialog->priv->word); + if (prev_word && *prev_word) { + html_editor_spell_check_dialog_set_word (dialog, prev_word); + g_free (prev_word); + return TRUE; } + g_free (prev_word); /* Close the dialog */ gtk_widget_hide (GTK_WIDGET (dialog)); return FALSE; } +static gboolean +html_editor_spell_check_dialog_next_idle_cb (gpointer user_data) +{ + EHTMLEditorSpellCheckDialog *dialog = user_data; + + g_return_val_if_fail (E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG (dialog), FALSE); + + html_editor_spell_check_dialog_next (dialog); + g_object_unref (dialog); + + return FALSE; +} + static void html_editor_spell_check_dialog_replace (EHTMLEditorSpellCheckDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *editor_selection; + EContentEditor *cnt_editor; GtkTreeModel *model; GtkTreeSelection *selection; GtkTreeIter iter; gchar *replacement; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - editor_selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (dialog->priv->tree_view)); @@ -343,47 +206,43 @@ html_editor_spell_check_dialog_replace (EHTMLEditorSpellCheckDialog *dialog) return; gtk_tree_model_get (model, &iter, 0, &replacement, -1); - e_html_editor_selection_insert_html ( - editor_selection, replacement); + e_content_editor_insert_content ( + cnt_editor, + replacement, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN); g_free (replacement); - html_editor_spell_check_dialog_next (dialog); + + g_idle_add (html_editor_spell_check_dialog_next_idle_cb, g_object_ref (dialog)); } static void html_editor_spell_check_dialog_replace_all (EHTMLEditorSpellCheckDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *editor_selection; + EContentEditor *cnt_editor; GtkTreeModel *model; GtkTreeSelection *selection; GtkTreeIter iter; gchar *replacement; - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - editor_selection = e_html_editor_view_get_selection (view); - selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (dialog->priv->tree_view)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, 0, &replacement, -1); - /* Repeatedly search for 'word', then replace selection by - * 'replacement'. Repeat until there's at least one occurrence of - * 'word' in the document */ - while (webkit_web_view_search_text ( - WEBKIT_WEB_VIEW (view), dialog->priv->word, - FALSE, TRUE, TRUE)) { + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_selection_insert_html ( - editor_selection, replacement); - } + e_content_editor_replace_all ( + cnt_editor, + E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE | + E_CONTENT_EDITOR_FIND_WRAP_AROUND, + dialog->priv->word, + replacement); - g_free (replacement); - html_editor_spell_check_dialog_next (dialog); + g_idle_add (html_editor_spell_check_dialog_next_idle_cb, g_object_ref (dialog)); } static void @@ -391,7 +250,6 @@ html_editor_spell_check_dialog_ignore (EHTMLEditorSpellCheckDialog *dialog) { if (dialog->priv->word == NULL) return; - e_spell_dictionary_ignore_word ( dialog->priv->current_dict, dialog->priv->word, -1); @@ -404,8 +262,7 @@ html_editor_spell_check_dialog_learn (EHTMLEditorSpellCheckDialog *dialog) if (dialog->priv->word == NULL) return; - e_spell_dictionary_learn_word ( - dialog->priv->current_dict, dialog->priv->word, -1); + e_spell_dictionary_learn_word (dialog->priv->current_dict, dialog->priv->word, -1); html_editor_spell_check_dialog_next (dialog); } @@ -434,45 +291,54 @@ html_editor_spell_check_dialog_set_dictionary (EHTMLEditorSpellCheckDialog *dial static void html_editor_spell_check_dialog_show (GtkWidget *widget) { - EHTMLEditor *editor; - EHTMLEditorView *view; EHTMLEditorSpellCheckDialog *dialog; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (widget); g_free (dialog->priv->word); dialog->priv->word = NULL; - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dialog->priv->selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - /* Select the first word or quit */ if (html_editor_spell_check_dialog_next (dialog)) { - GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)-> - show (widget); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_on_spell_check_dialog_open (cnt_editor); + + GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->show (widget); } } static void +html_editor_spell_check_dialog_hide (GtkWidget *widget) +{ + EContentEditor *cnt_editor; + EHTMLEditor *editor; + EHTMLEditorSpellCheckDialog *dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (widget); + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_on_spell_check_dialog_close (cnt_editor); + + /* Chain up to parent implementation */ + GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->hide (widget); +} + +static void html_editor_spell_check_dialog_finalize (GObject *object) { EHTMLEditorSpellCheckDialogPrivate *priv; priv = E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (object); - g_clear_object (&priv->selection); g_free (priv->word); /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)-> - finalize (object); + G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->finalize (object); } static void @@ -484,6 +350,7 @@ html_editor_spell_check_dialog_constructed (GObject *object) G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->constructed (object); dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (object); + e_html_editor_spell_check_dialog_update_dictionaries (dialog); } @@ -502,6 +369,7 @@ e_html_editor_spell_check_dialog_class_init (EHTMLEditorSpellCheckDialogClass *c widget_class = GTK_WIDGET_CLASS (class); widget_class->show = html_editor_spell_check_dialog_show; + widget_class->hide = html_editor_spell_check_dialog_hide; } static void @@ -644,10 +512,10 @@ void e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; ESpellChecker *spell_checker; GtkComboBox *combo_box; - GtkListStore *store; + GtkListStore *store = NULL; GQueue queue = G_QUEUE_INIT; gchar **languages; guint n_languages = 0; @@ -656,8 +524,8 @@ e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialo g_return_if_fail (E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG (dialog)); editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - spell_checker = e_html_editor_view_get_spell_checker (view); + cnt_editor = e_html_editor_get_content_editor (editor); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); languages = e_spell_checker_list_active_languages ( spell_checker, &n_languages); @@ -684,7 +552,7 @@ e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialo while (!g_queue_is_empty (&queue)) { ESpellDictionary *dictionary; GtkTreeIter iter; - const gchar *name; + const gchar *name = NULL; dictionary = g_queue_pop_head (&queue); name = e_spell_dictionary_get_name (dictionary); @@ -705,5 +573,6 @@ e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialo gtk_combo_box_set_active (combo_box, 0); g_object_unref (store); + g_clear_object (&spell_checker); } diff --git a/e-util/e-html-editor-table-dialog.c b/e-util/e-html-editor-table-dialog.c index f7155d6..370b72f 100644 --- a/e-util/e-html-editor-table-dialog.c +++ b/e-util/e-html-editor-table-dialog.c @@ -28,7 +28,6 @@ #include "e-color-combo.h" #include "e-dialog-widgets.h" -#include "e-html-editor-utils.h" #include "e-image-chooser-dialog.h" #include "e-misc-utils.h" @@ -50,15 +49,11 @@ struct _EHTMLEditorTableDialogPrivate { GtkWidget *alignment_combo; - GtkWidget *background_color_button; - GtkWidget *background_image_button; + GtkWidget *background_color_picker; + GtkWidget *background_image_chooser; GtkWidget *image_chooser_dialog; GtkWidget *remove_image_button; - - WebKitDOMHTMLTableElement *table_element; - - EHTMLEditorViewHistoryEvent *history_event; }; static GdkRGBA transparent = { 0, 0, 0, 0 }; @@ -68,267 +63,145 @@ G_DEFINE_TYPE ( e_html_editor_table_dialog, E_TYPE_HTML_EDITOR_DIALOG); -static WebKitDOMElement * -html_editor_table_dialog_create_table (EHTMLEditorTableDialog *dialog) +static void +html_editor_table_dialog_set_row_count (EHTMLEditorTableDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorSelection *editor_selection; - EHTMLEditorView *view; - gint i; - gchar *text_content; - gboolean empty = FALSE; - WebKitDOMDocument *document; - WebKitDOMElement *table, *br, *caret, *element, *cell; - WebKitDOMNode *clone; + EContentEditor *cnt_editor; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - editor_selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - /* Default 3x3 table */ - table = webkit_dom_document_create_element (document, "TABLE", NULL); - for (i = 0; i < 3; i++) { - WebKitDOMHTMLElement *row; - gint j; - - row = webkit_dom_html_table_element_insert_row ( - WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL); - - for (j = 0; j < 3; j++) { - webkit_dom_html_table_row_element_insert_cell ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL); - } - } + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_selection_save (editor_selection); - caret = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - element = get_parent_block_element (WEBKIT_DOM_NODE (caret)); - text_content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (element)); - empty = text_content && !*text_content; - g_free (text_content); - - clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), FALSE); - br = webkit_dom_document_create_element (document, "BR", NULL); - webkit_dom_node_append_child (clone, WEBKIT_DOM_NODE (br), NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - clone, - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)), - NULL); - - /* Move caret to the first cell */ - cell = webkit_dom_element_query_selector (table, "td", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (cell), WEBKIT_DOM_NODE (caret), NULL); - caret = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (cell), - WEBKIT_DOM_NODE (caret), - webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (cell)), - NULL); - - /* Insert the table into body unred the current block (if current block is not empty) - * otherwise replace the current block. */ - if (empty) { - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - WEBKIT_DOM_NODE (table), - WEBKIT_DOM_NODE (element), - NULL); - } else { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - WEBKIT_DOM_NODE (table), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)), - NULL); - } - - e_html_editor_selection_restore (editor_selection); - - e_html_editor_view_set_changed (view, TRUE); - - return table; -} - -static void -html_editor_table_dialog_set_row_count (EHTMLEditorTableDialog *dialog) -{ - WebKitDOMHTMLCollection *rows; - gulong ii, current_count, expected_count; - - g_return_if_fail (dialog->priv->table_element); - - rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element); - current_count = webkit_dom_html_collection_get_length (rows); - expected_count = gtk_spin_button_get_value ( - GTK_SPIN_BUTTON (dialog->priv->rows_edit)); - - if (current_count < expected_count) { - for (ii = 0; ii < expected_count - current_count; ii++) { - webkit_dom_html_table_element_insert_row ( - dialog->priv->table_element, -1, NULL); - } - } else if (current_count > expected_count) { - for (ii = 0; ii < current_count - expected_count; ii++) { - webkit_dom_html_table_element_delete_row ( - dialog->priv->table_element, -1, NULL); - } - } - g_object_unref (rows); + e_content_editor_table_set_row_count ( + cnt_editor, + gtk_spin_button_get_value ( + GTK_SPIN_BUTTON (dialog->priv->rows_edit))); } static void html_editor_table_dialog_get_row_count (EHTMLEditorTableDialog *dialog) { - WebKitDOMHTMLCollection *rows; - - g_return_if_fail (dialog->priv->table_element); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); gtk_spin_button_set_value ( GTK_SPIN_BUTTON (dialog->priv->rows_edit), - webkit_dom_html_collection_get_length (rows)); - g_object_unref (rows); + e_content_editor_table_get_row_count (cnt_editor)); } static void html_editor_table_dialog_set_column_count (EHTMLEditorTableDialog *dialog) { - WebKitDOMHTMLCollection *rows; - gulong ii, row_count, expected_columns; - - g_return_if_fail (dialog->priv->table_element); - - rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element); - row_count = webkit_dom_html_collection_get_length (rows); - expected_columns = gtk_spin_button_get_value ( - GTK_SPIN_BUTTON (dialog->priv->columns_edit)); - - for (ii = 0; ii < row_count; ii++) { - WebKitDOMHTMLTableRowElement *row; - WebKitDOMHTMLCollection *cells; - gulong jj, current_columns; - - row = WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT ( - webkit_dom_html_collection_item (rows, ii)); - - cells = webkit_dom_html_table_row_element_get_cells (row); - current_columns = webkit_dom_html_collection_get_length (cells); - - if (current_columns < expected_columns) { - for (jj = 0; jj < expected_columns - current_columns; jj++) { - webkit_dom_html_table_row_element_insert_cell ( - row, -1, NULL); - } - } else if (expected_columns < current_columns) { - for (jj = 0; jj < current_columns - expected_columns; jj++) { - webkit_dom_html_table_row_element_delete_cell ( - row, -1, NULL); - } - } - g_object_unref (row); - g_object_unref (cells); - } - g_object_unref (rows); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_table_set_column_count ( + cnt_editor, + gtk_spin_button_get_value ( + GTK_SPIN_BUTTON (dialog->priv->columns_edit))); } static void html_editor_table_dialog_get_column_count (EHTMLEditorTableDialog *dialog) { - WebKitDOMHTMLCollection *rows, *columns; - WebKitDOMNode *row; - - g_return_if_fail (dialog->priv->table_element); - - rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element); - row = webkit_dom_html_collection_item (rows, 0); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - columns = webkit_dom_html_table_row_element_get_cells ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); gtk_spin_button_set_value ( GTK_SPIN_BUTTON (dialog->priv->columns_edit), - webkit_dom_html_collection_get_length (columns)); - g_object_unref (row); - g_object_unref (rows); - g_object_unref (columns); + e_content_editor_table_get_column_count (cnt_editor)); } static void html_editor_table_dialog_set_width (EHTMLEditorTableDialog *dialog) { - gchar *width; + EHTMLEditor *editor; + EContentEditor *cnt_editor; - g_return_if_fail (dialog->priv->table_element); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); if (gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON (dialog->priv->width_check))) { - gchar *units; - units = gtk_combo_box_text_get_active_text ( - GTK_COMBO_BOX_TEXT (dialog->priv->width_units)); - width = g_strdup_printf ( - "%d%s", + e_content_editor_table_set_width ( + cnt_editor, gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON (dialog->priv->width_edit)), - units); - g_free (units); + (gtk_combo_box_get_active ( + GTK_COMBO_BOX (dialog->priv->width_units)) == 0) ? + E_CONTENT_EDITOR_UNIT_PIXEL : + E_CONTENT_EDITOR_UNIT_PERCENTAGE); gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE); gtk_widget_set_sensitive (dialog->priv->width_units, TRUE); } else { - width = g_strdup ("auto"); + e_content_editor_table_set_width ( + cnt_editor, 0, E_CONTENT_EDITOR_UNIT_AUTO); gtk_widget_set_sensitive (dialog->priv->width_edit, FALSE); gtk_widget_set_sensitive (dialog->priv->width_units, FALSE); } +} - webkit_dom_html_table_element_set_width ( - dialog->priv->table_element, width); - g_free (width); +static void +html_editor_table_dialog_width_units_changed (GtkWidget *widget, + EHTMLEditorTableDialog *dialog) +{ + if (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->width_units)) == 0) { + gtk_spin_button_set_range ( + GTK_SPIN_BUTTON (dialog->priv->width_edit), 0, G_MAXUINT); + } else + gtk_spin_button_set_range ( + GTK_SPIN_BUTTON (dialog->priv->width_edit), 0, 100); + + html_editor_table_dialog_set_width (dialog); } static void html_editor_table_dialog_get_width (EHTMLEditorTableDialog *dialog) { - gchar *width; - - width = webkit_dom_html_table_element_get_width (dialog->priv->table_element); - if (!width || !*width || g_ascii_strncasecmp (width, "auto", 4) == 0) { - gtk_toggle_button_set_active ( - GTK_TOGGLE_BUTTON (dialog->priv->width_check), FALSE); - gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->width_edit), 100); - gtk_combo_box_set_active_id ( - GTK_COMBO_BOX (dialog->priv->width_units), "units-percent"); - } else { - gint width_int = atoi (width); - - gtk_toggle_button_set_active ( - GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE); - gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->width_edit), width_int); - gtk_combo_box_set_active_id ( - GTK_COMBO_BOX (dialog->priv->width_units), - ((strstr (width, "%") == NULL) ? - "units-px" : "units-percent")); - } - g_free (width); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + EContentEditorUnit unit; + gint width; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + width = e_content_editor_table_get_width (cnt_editor, &unit); + + gtk_toggle_button_set_active ( + GTK_TOGGLE_BUTTON (dialog->priv->width_check), + unit != E_CONTENT_EDITOR_UNIT_AUTO); + gtk_spin_button_set_value ( + GTK_SPIN_BUTTON (dialog->priv->width_edit), + unit == E_CONTENT_EDITOR_UNIT_AUTO ? 100 : width); + gtk_combo_box_set_active_id ( + GTK_COMBO_BOX (dialog->priv->width_units), + unit == E_CONTENT_EDITOR_UNIT_PIXEL ? "units-px" : "units-percent"); } static void html_editor_table_dialog_set_alignment (EHTMLEditorTableDialog *dialog) { - g_return_if_fail (dialog->priv->table_element); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - webkit_dom_html_table_element_set_align ( - dialog->priv->table_element, + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_table_set_align ( + cnt_editor, gtk_combo_box_get_active_id ( GTK_COMBO_BOX (dialog->priv->alignment_combo))); } @@ -336,208 +209,150 @@ html_editor_table_dialog_set_alignment (EHTMLEditorTableDialog *dialog) static void html_editor_table_dialog_get_alignment (EHTMLEditorTableDialog *dialog) { - gchar *alignment; - - g_return_if_fail (dialog->priv->table_element); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gchar *value; - alignment = webkit_dom_html_table_element_get_align ( - dialog->priv->table_element); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + value = e_content_editor_table_get_align (cnt_editor); gtk_combo_box_set_active_id ( - GTK_COMBO_BOX (dialog->priv->alignment_combo), alignment); - - g_free (alignment); + GTK_COMBO_BOX (dialog->priv->alignment_combo), value); + g_free (value); } static void html_editor_table_dialog_set_padding (EHTMLEditorTableDialog *dialog) { - gchar *padding; - - g_return_if_fail (dialog->priv->table_element); - - padding = g_strdup_printf ( - "%d", - gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->padding_edit))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - webkit_dom_html_table_element_set_cell_padding ( - dialog->priv->table_element, padding); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_free (padding); + e_content_editor_table_set_padding ( + cnt_editor, + gtk_spin_button_get_value_as_int ( + GTK_SPIN_BUTTON (dialog->priv->padding_edit))); } static void html_editor_table_dialog_get_padding (EHTMLEditorTableDialog *dialog) { - gchar *padding; - gint padding_int; - - g_return_if_fail (dialog->priv->table_element); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - padding = webkit_dom_html_table_element_get_cell_padding ( - dialog->priv->table_element); - if (!padding || !*padding) { - padding_int = 0; - } else { - padding_int = atoi (padding); - } + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->padding_edit), padding_int); - - g_free (padding); + GTK_SPIN_BUTTON (dialog->priv->padding_edit), + e_content_editor_table_get_padding (cnt_editor)); } static void html_editor_table_dialog_set_spacing (EHTMLEditorTableDialog *dialog) { - gchar *spacing; - - g_return_if_fail (dialog->priv->table_element); - - spacing = g_strdup_printf ( - "%d", - gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->spacing_edit))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - webkit_dom_html_table_element_set_cell_spacing ( - dialog->priv->table_element, spacing); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_free (spacing); + e_content_editor_table_set_spacing ( + cnt_editor, + gtk_spin_button_get_value_as_int ( + GTK_SPIN_BUTTON (dialog->priv->spacing_edit))); } static void html_editor_table_dialog_get_spacing (EHTMLEditorTableDialog *dialog) { - gchar *spacing; - gint spacing_int; - - g_return_if_fail (dialog->priv->table_element); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - spacing = webkit_dom_html_table_element_get_cell_spacing ( - dialog->priv->table_element); - if (!spacing || !*spacing) { - spacing_int = 0; - } else { - spacing_int = atoi (spacing); - } + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->spacing_edit), spacing_int); - - g_free (spacing); + GTK_SPIN_BUTTON (dialog->priv->spacing_edit), + e_content_editor_table_get_spacing (cnt_editor)); } static void html_editor_table_dialog_set_border (EHTMLEditorTableDialog *dialog) { - gchar *border; - - g_return_if_fail (dialog->priv->table_element); - - border = g_strdup_printf ( - "%d", - gtk_spin_button_get_value_as_int ( - GTK_SPIN_BUTTON (dialog->priv->border_edit))); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - webkit_dom_html_table_element_set_border ( - dialog->priv->table_element, border); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_free (border); + e_content_editor_table_set_border ( + cnt_editor, + gtk_spin_button_get_value_as_int ( + GTK_SPIN_BUTTON (dialog->priv->border_edit))); } static void html_editor_table_dialog_get_border (EHTMLEditorTableDialog *dialog) { - gchar *border; - gint border_int; - - g_return_if_fail (dialog->priv->table_element); + EHTMLEditor *editor; + EContentEditor *cnt_editor; - border = webkit_dom_html_table_element_get_border ( - dialog->priv->table_element); - if (!border || !*border) { - border_int = 0; - } else { - border_int = atoi (border); - } + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); gtk_spin_button_set_value ( - GTK_SPIN_BUTTON (dialog->priv->border_edit), border_int); - - g_free (border); + GTK_SPIN_BUTTON (dialog->priv->border_edit), + e_content_editor_table_get_border (cnt_editor)); } static void html_editor_table_dialog_set_background_color (EHTMLEditorTableDialog *dialog) { - gchar *color; + EHTMLEditor *editor; + EContentEditor *cnt_editor; GdkRGBA rgba; - g_return_if_fail (dialog->priv->table_element); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); e_color_combo_get_current_color ( - E_COLOR_COMBO (dialog->priv->background_color_button), &rgba); - - if (rgba.alpha != 0.0) - color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba)); - else - color = g_strdup (""); - - webkit_dom_html_table_element_set_bg_color ( - dialog->priv->table_element, color); - - g_free (color); + E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba); + e_content_editor_table_set_background_color (cnt_editor, &rgba); } static void html_editor_table_dialog_get_background_color (EHTMLEditorTableDialog *dialog) { - gchar *color; + EHTMLEditor *editor; + EContentEditor *cnt_editor; GdkRGBA rgba; - g_return_if_fail (dialog->priv->table_element); - - color = webkit_dom_html_table_element_get_bg_color ( - dialog->priv->table_element); - - if (color && *color) { - gdk_rgba_parse (&rgba, color); - - e_color_combo_set_current_color ( - E_COLOR_COMBO (dialog->priv->background_color_button), &rgba); - } else { - e_color_combo_set_current_color ( - E_COLOR_COMBO (dialog->priv->background_color_button), &transparent); - } + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_free (color); + e_content_editor_table_get_background_color (cnt_editor, &rgba); + e_color_combo_set_current_color ( + E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba); } static void html_editor_table_dialog_set_background_image (EHTMLEditorTableDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; gchar *uri; - g_return_if_fail (dialog->priv->table_element); - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); uri = gtk_file_chooser_get_uri ( - GTK_FILE_CHOOSER (dialog->priv->background_image_button)); + GTK_FILE_CHOOSER (dialog->priv->background_image_chooser)); - if (uri && *uri) - e_html_editor_selection_replace_image_src ( - e_html_editor_view_get_selection (view), - WEBKIT_DOM_ELEMENT (dialog->priv->table_element), - uri); - else - remove_image_attributes_from_element ( - WEBKIT_DOM_ELEMENT (dialog->priv->table_element)); + e_content_editor_table_set_background_image_uri (cnt_editor, uri); gtk_widget_set_sensitive (dialog->priv->remove_image_button, uri && *uri); @@ -547,27 +362,22 @@ html_editor_table_dialog_set_background_image (EHTMLEditorTableDialog *dialog) static void html_editor_table_dialog_get_background_image (EHTMLEditorTableDialog *dialog) { - g_return_if_fail (dialog->priv->table_element); - - - if (!webkit_dom_element_has_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->table_element), "background")) { - - gtk_file_chooser_unselect_all ( - GTK_FILE_CHOOSER (dialog->priv->background_image_button)); - return; - } else { - gchar *value; + EHTMLEditor *editor; + EContentEditor *cnt_editor; + gchar *uri; - value = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (dialog->priv->table_element), "data-uri"); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + uri = e_content_editor_table_get_background_image_uri (cnt_editor); + if (uri && *uri) gtk_file_chooser_set_uri ( - GTK_FILE_CHOOSER (dialog->priv->background_image_button), - value); + GTK_FILE_CHOOSER (dialog->priv->background_image_chooser), uri); + else + gtk_file_chooser_unselect_all ( + GTK_FILE_CHOOSER (dialog->priv->background_image_chooser)); - g_free (value); - } + g_free (uri); } static void @@ -609,10 +419,10 @@ html_editor_table_dialog_reset_values (EHTMLEditorTableDialog *dialog) GTK_SPIN_BUTTON (dialog->priv->border_edit), 1); e_color_combo_set_current_color ( - E_COLOR_COMBO (dialog->priv->background_color_button), &transparent); + E_COLOR_COMBO (dialog->priv->background_color_picker), &transparent); gtk_file_chooser_unselect_all ( - GTK_FILE_CHOOSER (dialog->priv->background_image_button)); + GTK_FILE_CHOOSER (dialog->priv->background_image_chooser)); html_editor_table_dialog_set_row_count (dialog); html_editor_table_dialog_set_column_count (dialog); @@ -630,56 +440,16 @@ html_editor_table_dialog_show (GtkWidget *widget) { EHTMLEditorTableDialog *dialog; EHTMLEditor *editor; - EHTMLEditorView *view; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; + EContentEditor *cnt_editor; dialog = E_HTML_EDITOR_TABLE_DIALOG (widget); editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) { - WebKitDOMElement *table; - WebKitDOMRange *range; - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - table = e_html_editor_dom_node_find_parent_element ( - webkit_dom_range_get_start_container (range, NULL), "TABLE"); - g_object_unref (range); - - if (!table) { - dialog->priv->table_element = WEBKIT_DOM_HTML_TABLE_ELEMENT ( - html_editor_table_dialog_create_table (dialog)); - html_editor_table_dialog_reset_values (dialog); - } else { - dialog->priv->table_element = - WEBKIT_DOM_HTML_TABLE_ELEMENT (table); - html_editor_table_dialog_get_values (dialog); - } - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - EHTMLEditorViewHistoryEvent *ev; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_TABLE_DIALOG; - - e_html_editor_selection_get_selection_coordinates ( - e_html_editor_view_get_selection (view), - &ev->before.start.x, &ev->before.start.y, - &ev->before.end.x, &ev->before.end.y); - if (table) - ev->data.dom.from = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (table), TRUE); - dialog->priv->history_event = ev; - } - } + cnt_editor = e_html_editor_get_content_editor (editor); - g_object_unref (dom_selection); + if (e_content_editor_on_table_dialog_open (cnt_editor)) + html_editor_table_dialog_reset_values (dialog); + else + html_editor_table_dialog_get_values (dialog); /* Chain up to parent implementation */ GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->show (widget); @@ -688,11 +458,16 @@ html_editor_table_dialog_show (GtkWidget *widget) static void html_editor_table_dialog_remove_image (EHTMLEditorTableDialog *dialog) { - remove_image_attributes_from_element ( - WEBKIT_DOM_ELEMENT (dialog->priv->table_element)); + EHTMLEditor *editor; + EContentEditor *cnt_editor; + + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_table_set_background_image_uri (cnt_editor, NULL); gtk_file_chooser_unselect_all ( - GTK_FILE_CHOOSER (dialog->priv->background_image_button)); + GTK_FILE_CHOOSER (dialog->priv->background_image_chooser)); gtk_widget_set_sensitive (dialog->priv->remove_image_button, FALSE); } @@ -700,39 +475,15 @@ html_editor_table_dialog_remove_image (EHTMLEditorTableDialog *dialog) static void html_editor_table_dialog_hide (GtkWidget *widget) { - EHTMLEditorTableDialogPrivate *priv; - EHTMLEditorViewHistoryEvent *ev; - - priv = E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE (widget); - ev = priv->history_event; - - if (ev) { - EHTMLEditorTableDialog *dialog; - EHTMLEditor *editor; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; - - dialog = E_HTML_EDITOR_TABLE_DIALOG (widget); - editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - ev->data.dom.to = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (priv->table_element), TRUE); - - if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) { - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } else { - g_object_unref (ev->data.dom.from); - g_object_unref (ev->data.dom.to); - g_free (ev); - } - } + EHTMLEditorTableDialog *dialog; + EHTMLEditor *editor; + EContentEditor *cnt_editor; + + dialog = E_HTML_EDITOR_TABLE_DIALOG (widget); + editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); + cnt_editor = e_html_editor_get_content_editor (editor); - g_object_unref (priv->table_element); - priv->table_element = NULL; + e_content_editor_on_table_dialog_close (cnt_editor); GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->hide (widget); } @@ -839,9 +590,9 @@ e_html_editor_table_dialog_init (EHTMLEditorTableDialog *dialog) gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px"); gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%"); gtk_grid_attach (grid, widget, 2, 0, 1, 1); - g_signal_connect_swapped ( + g_signal_connect ( widget, "changed", - G_CALLBACK (html_editor_table_dialog_set_width), dialog); + G_CALLBACK (html_editor_table_dialog_width_units_changed), dialog); dialog->priv->width_units = widget; /* Spacing */ @@ -932,12 +683,12 @@ e_html_editor_table_dialog_init (EHTMLEditorTableDialog *dialog) g_signal_connect_swapped ( widget, "notify::current-color", G_CALLBACK (html_editor_table_dialog_set_background_color), dialog); - dialog->priv->background_color_button = widget; + dialog->priv->background_color_picker = widget; widget = gtk_label_new_with_mnemonic (_("_Color:")); gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT); gtk_label_set_mnemonic_widget ( - GTK_LABEL (widget), dialog->priv->background_color_button); + GTK_LABEL (widget), dialog->priv->background_color_picker); gtk_grid_attach (grid, widget, 0, 0, 1, 1); /* Image */ @@ -958,12 +709,12 @@ e_html_editor_table_dialog_init (EHTMLEditorTableDialog *dialog) g_signal_connect_swapped ( widget, "file-set", G_CALLBACK (html_editor_table_dialog_set_background_image), dialog); - dialog->priv->background_image_button = widget; + dialog->priv->background_image_chooser = widget; widget =gtk_label_new_with_mnemonic (_("Image:")); gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT); gtk_label_set_mnemonic_widget ( - GTK_LABEL (widget), dialog->priv->background_image_button); + GTK_LABEL (widget), dialog->priv->background_image_chooser); gtk_grid_attach (grid, widget, 0, 1, 1, 1); box = e_html_editor_dialog_get_button_box (E_HTML_EDITOR_DIALOG (dialog)); @@ -990,4 +741,3 @@ e_html_editor_table_dialog_new (EHTMLEditor *editor) "title", _("Table Properties"), NULL)); } - diff --git a/e-util/e-html-editor-text-dialog.c b/e-util/e-html-editor-text-dialog.c index 9edb8e5..f944338 100644 --- a/e-util/e-html-editor-text-dialog.c +++ b/e-util/e-html-editor-text-dialog.c @@ -51,15 +51,13 @@ static void html_editor_text_dialog_set_bold (EHTMLEditorTextDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_selection_set_bold ( - selection, + e_content_editor_set_bold ( + cnt_editor, gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON (dialog->priv->bold_check))); } @@ -68,15 +66,13 @@ static void html_editor_text_dialog_set_italic (EHTMLEditorTextDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_selection_set_italic ( - selection, + e_content_editor_set_italic ( + cnt_editor, gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON (dialog->priv->italic_check))); } @@ -85,15 +81,13 @@ static void html_editor_text_dialog_set_underline (EHTMLEditorTextDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_selection_set_underline ( - selection, + e_content_editor_set_underline ( + cnt_editor, gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON (dialog->priv->underline_check))); } @@ -102,15 +96,13 @@ static void html_editor_text_dialog_set_strikethrough (EHTMLEditorTextDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_selection_set_strikethrough ( - selection, + e_content_editor_set_strikethrough ( + cnt_editor, gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON (dialog->priv->strikethrough_check))); } @@ -119,33 +111,29 @@ static void html_editor_text_dialog_set_color (EHTMLEditorTextDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; GdkRGBA rgba; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); e_color_combo_get_current_color ( E_COLOR_COMBO (dialog->priv->color_check), &rgba); - e_html_editor_selection_set_font_color (selection, &rgba); + e_content_editor_set_font_color (cnt_editor, &rgba); } static void html_editor_text_dialog_set_size (EHTMLEditorTextDialog *dialog) { EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; gint size; editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); size = gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->size_check)); - e_html_editor_selection_set_font_size (selection, size + 1); + e_content_editor_set_font_size (cnt_editor, size + 1); } static void @@ -153,35 +141,36 @@ html_editor_text_dialog_show (GtkWidget *widget) { EHTMLEditorTextDialog *dialog; EHTMLEditor *editor; - EHTMLEditorView *view; - EHTMLEditorSelection *selection; - GdkRGBA rgba; + EContentEditor *cnt_editor; + GdkRGBA *rgba; dialog = E_HTML_EDITOR_TEXT_DIALOG (widget); editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog)); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); + cnt_editor = e_html_editor_get_content_editor (editor); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON (dialog->priv->bold_check), - e_html_editor_selection_is_bold (selection)); + e_content_editor_is_bold (cnt_editor)); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON (dialog->priv->italic_check), - e_html_editor_selection_is_italic (selection)); + e_content_editor_is_italic (cnt_editor)); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON (dialog->priv->underline_check), - e_html_editor_selection_is_underline (selection)); + e_content_editor_is_underline (cnt_editor)); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON (dialog->priv->strikethrough_check), - e_html_editor_selection_is_strikethrough (selection)); + e_content_editor_is_strikethrough (cnt_editor)); gtk_combo_box_set_active ( GTK_COMBO_BOX (dialog->priv->size_check), - e_html_editor_selection_get_font_size (selection) - 1); - - e_html_editor_selection_get_font_color (selection, &rgba); - e_color_combo_set_current_color ( - E_COLOR_COMBO (dialog->priv->color_check), &rgba); + e_content_editor_get_font_size (cnt_editor) - 1); + + rgba = e_content_editor_dup_font_color (cnt_editor); + if (rgba) { + e_color_combo_set_current_color ( + E_COLOR_COMBO (dialog->priv->color_check), rgba); + gdk_rgba_free (rgba); + } GTK_WIDGET_CLASS (e_html_editor_text_dialog_parent_class)->show (widget); } diff --git a/e-util/e-html-editor-utils.c b/e-util/e-html-editor-utils.c deleted file mode 100644 index 391fb6a..0000000 --- a/e-util/e-html-editor-utils.c +++ /dev/null @@ -1,653 +0,0 @@ -/* - * e-html-editor-utils.c - * - * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com> - * - * 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 <http://www.gnu.org/licenses/> - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "e-html-editor-utils.h" -#include "e-misc-utils.h" -#include <string.h> - -/** - * e_html_editor_dom_node_find_parent_element: - * @node: Start node - * @tagname: Tag name of element to search - * - * Recursively searches for first occurance of element with given @tagname - * that is parent of given @node. - * - * Returns: A #WebKitDOMElement with @tagname representing parent of @node or - * @NULL when @node has no parent with given @tagname. When @node matches @tagname, - * then the @node is returned. - */ -WebKitDOMElement * -e_html_editor_dom_node_find_parent_element (WebKitDOMNode *node, - const gchar *tagname) -{ - gint taglen = strlen (tagname); - - while (node) { - - if (WEBKIT_DOM_IS_ELEMENT (node)) { - gchar *node_tagname; - - node_tagname = webkit_dom_element_get_tag_name ( - WEBKIT_DOM_ELEMENT (node)); - - if (node_tagname && - (strlen (node_tagname) == taglen) && - (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) { - g_free (node_tagname); - return WEBKIT_DOM_ELEMENT (node); - } - - g_free (node_tagname); - } - - node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node)); - } - - return NULL; -} - -/** - * e_html_editor_dom_node_find_child_element: - * @node: Start node - * @tagname: Tag name of element to search. - * - * Recursively searches for first occurrence of element with given @tagname that - * is a child of @node. - * - * Returns: A #WebKitDOMElement with @tagname representing a child of @node or - * @NULL when @node has no child with given @tagname. When @node matches @tagname, - * then the @node is returned. - */ -WebKitDOMElement * -e_html_editor_dom_node_find_child_element (WebKitDOMNode *node, - const gchar *tagname) -{ - WebKitDOMNode *start_node = node; - gint taglen = strlen (tagname); - - do { - if (WEBKIT_DOM_IS_ELEMENT (node)) { - gchar *node_tagname; - - node_tagname = webkit_dom_element_get_tag_name ( - WEBKIT_DOM_ELEMENT (node)); - - if (node_tagname && - (strlen (node_tagname) == taglen) && - (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) { - g_free (node_tagname); - return WEBKIT_DOM_ELEMENT (node); - } - - g_free (node_tagname); - } - - if (webkit_dom_node_has_child_nodes (node)) { - node = webkit_dom_node_get_first_child (node); - } else if (webkit_dom_node_get_next_sibling (node)) { - node = webkit_dom_node_get_next_sibling (node); - } else { - node = webkit_dom_node_get_parent_node (node); - } - } while (!webkit_dom_node_is_same_node (node, start_node)); - - return NULL; -} - -gboolean -e_html_editor_node_is_selection_position_node (WebKitDOMNode *node) -{ - WebKitDOMElement *element; - - if (!node || !WEBKIT_DOM_IS_ELEMENT (node)) - return FALSE; - - element = WEBKIT_DOM_ELEMENT (node); - - return element_has_id (element, "-x-evo-selection-start-marker") || - element_has_id (element, "-x-evo-selection-end-marker"); -} - -WebKitDOMNode * -e_html_editor_get_parent_block_node_from_child (WebKitDOMNode *node) -{ - WebKitDOMNode *parent = node; - - if (!WEBKIT_DOM_IS_ELEMENT (parent) || - e_html_editor_node_is_selection_position_node (parent)) - parent = webkit_dom_node_get_parent_node (parent); - - if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-temp-text-wrapper") || - element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted") || - element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quote-character") || - element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-signature") || - element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-resizable-wrapper") || - WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) || - element_has_tag (WEBKIT_DOM_ELEMENT (parent), "b") || - element_has_tag (WEBKIT_DOM_ELEMENT (parent), "i") || - element_has_tag (WEBKIT_DOM_ELEMENT (parent), "u")) - parent = webkit_dom_node_get_parent_node (parent); - - if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted") || - element_has_class (WEBKIT_DOM_ELEMENT (parent), "Apple-tab-span")) - parent = webkit_dom_node_get_parent_node (parent); - - return parent; -} - -gboolean -element_has_id (WebKitDOMElement *element, - const gchar* id) -{ - gchar *element_id; - - if (!element) - return FALSE; - - if (!WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - element_id = webkit_dom_element_get_id (element); - - if (g_ascii_strcasecmp (element_id, id) != 0) { - g_free (element_id); - return FALSE; - } - g_free (element_id); - - return TRUE; -} - -gboolean -element_has_tag (WebKitDOMElement *element, - const gchar* tag) -{ - gchar *element_tag; - - if (!WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - element_tag = webkit_dom_node_get_local_name (WEBKIT_DOM_NODE (element)); - - if (g_ascii_strcasecmp (element_tag, tag) != 0) { - g_free (element_tag); - return FALSE; - } - g_free (element_tag); - - return TRUE; -} - -gboolean -element_has_class (WebKitDOMElement *element, - const gchar* class) -{ - gchar *element_class; - - if (!element) - return FALSE; - - if (!WEBKIT_DOM_IS_ELEMENT (element)) - return FALSE; - - element_class = webkit_dom_element_get_class_name (element); - - if (g_strstr_len (element_class, -1, class)) { - g_free (element_class); - return TRUE; - } - g_free (element_class); - - return FALSE; -} - -void -element_add_class (WebKitDOMElement *element, - const gchar* class) -{ - gchar *element_class; - gchar *new_class; - - if (!WEBKIT_DOM_IS_ELEMENT (element)) - return; - - if (element_has_class (element, class)) - return; - - element_class = webkit_dom_element_get_class_name (element); - - if (g_strcmp0 (element_class, "") == 0) - new_class = g_strdup (class); - else - new_class = g_strconcat (element_class, " ", class, NULL); - - webkit_dom_element_set_class_name (element, new_class); - - g_free (element_class); - g_free (new_class); -} - -void -element_remove_class (WebKitDOMElement *element, - const gchar* class) -{ - gchar *element_class, *final_class; - GRegex *regex; - gchar *pattern = NULL; - - if (!WEBKIT_DOM_IS_ELEMENT (element)) - return; - - if (!element_has_class (element, class)) - return; - - element_class = webkit_dom_element_get_class_name (element); - - pattern = g_strconcat ("[\\s]*", class, "[\\s]*", NULL); - regex = g_regex_new (pattern, 0, 0, NULL); - final_class = g_regex_replace (regex, element_class, -1, 0, " ", 0, NULL); - - if (g_strcmp0 (final_class, " ") != 0) - webkit_dom_element_set_class_name (element, final_class); - else - webkit_dom_element_remove_attribute (element, "class"); - - g_free (element_class); - g_free (final_class); - g_free (pattern); - g_regex_unref (regex); -} - -void -remove_node (WebKitDOMNode *node) -{ - WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); - - /* Check if the parent exists, if so it means that the node is still - * in the DOM or at least the parent is. If it doesn't exists it is not - * in the DOM and we can free it. */ - if (parent) - webkit_dom_node_remove_child (parent, node, NULL); - else - g_object_unref (node); -} - -void -remove_node_if_empty (WebKitDOMNode *node) -{ - WebKitDOMNode *child; - - if (!WEBKIT_DOM_IS_NODE (node)) - return; - - if ((child = webkit_dom_node_get_first_child (node))) { - WebKitDOMNode *prev_sibling, *next_sibling; - - prev_sibling = webkit_dom_node_get_previous_sibling (child); - next_sibling = webkit_dom_node_get_next_sibling (child); - /* Empty or BR as sibling, but no sibling after it. */ - if (!webkit_dom_node_get_first_child (child) && - !WEBKIT_DOM_IS_TEXT (child) && - (!prev_sibling || - (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling) && - !webkit_dom_node_get_previous_sibling (prev_sibling))) && - (!next_sibling || - (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling) && - !webkit_dom_node_get_next_sibling (next_sibling)))) { - - remove_node (node); - } else { - gchar *text_content; - - text_content = webkit_dom_node_get_text_content (node); - if (!text_content) - remove_node (node); - - if (text_content && !*text_content) - remove_node (node); - - g_free (text_content); - } - } else - remove_node (node); -} - -WebKitDOMNode * -split_node_into_two (WebKitDOMNode *item, - gint level) -{ - gint current_level = 1; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - WebKitDOMNode *parent, *prev_parent = NULL, *tmp = NULL; - - document = webkit_dom_node_get_owner_document (item); - fragment = webkit_dom_document_create_document_fragment (document); - - tmp = item; - parent = webkit_dom_node_get_parent_node (item); - while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - WebKitDOMNode *clone, *first_child, *insert_before = NULL, *sibling; - - first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); - clone = webkit_dom_node_clone_node (parent, FALSE); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (fragment), clone, first_child, NULL); - - if (first_child) - insert_before = webkit_dom_node_get_first_child (first_child); - - while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child))) - webkit_dom_node_insert_before (first_child, sibling, insert_before, NULL); - - while (tmp && (sibling = webkit_dom_node_get_next_sibling (tmp))) - webkit_dom_node_append_child (clone, sibling, NULL); - - if (tmp) - webkit_dom_node_insert_before ( - clone, tmp, webkit_dom_node_get_first_child (clone), NULL); - - prev_parent = parent; - tmp = webkit_dom_node_get_next_sibling (parent); - parent = webkit_dom_node_get_parent_node (parent); - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); - insert_before = webkit_dom_node_get_first_child (first_child); - while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child))) { - webkit_dom_node_insert_before ( - first_child, sibling, insert_before, NULL); - } - } - - if (current_level >= level && level >= 0) - break; - - current_level++; - } - - if (prev_parent) { - tmp = webkit_dom_node_insert_before ( - parent, - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), - webkit_dom_node_get_next_sibling (prev_parent), - NULL); - remove_node_if_empty (prev_parent); - } - - return tmp; -} - -WebKitDOMElement * -create_selection_marker (WebKitDOMDocument *document, - gboolean start) -{ - WebKitDOMElement *element; - - element = webkit_dom_document_create_element ( - document, "SPAN", NULL); - webkit_dom_element_set_id ( - element, - start ? "-x-evo-selection-start-marker" : - "-x-evo-selection-end-marker"); - - return element; -} - -void -remove_selection_markers (WebKitDOMDocument *document) -{ - WebKitDOMElement *marker; - - marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (marker) - remove_node (WEBKIT_DOM_NODE (marker)); - marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - if (marker) - remove_node (WEBKIT_DOM_NODE (marker)); -} - -void -add_selection_markers_into_element_start (WebKitDOMDocument *document, - WebKitDOMElement *element, - WebKitDOMElement **selection_start_marker, - WebKitDOMElement **selection_end_marker) -{ - WebKitDOMElement *marker; - - remove_selection_markers (document); - marker = create_selection_marker (document, FALSE); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (marker), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)), - NULL); - if (selection_end_marker) - *selection_end_marker = marker; - - marker = create_selection_marker (document, TRUE); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (marker), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)), - NULL); - if (selection_start_marker) - *selection_start_marker = marker; -} - -void -add_selection_markers_into_element_end (WebKitDOMDocument *document, - WebKitDOMElement *element, - WebKitDOMElement **selection_start_marker, - WebKitDOMElement **selection_end_marker) -{ - WebKitDOMElement *marker; - - remove_selection_markers (document); - marker = create_selection_marker (document, TRUE); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL); - if (selection_start_marker) - *selection_start_marker = marker; - - marker = create_selection_marker (document, FALSE); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL); - if (selection_end_marker) - *selection_end_marker = marker; -} - -gboolean -node_is_list (WebKitDOMNode *node) -{ - return node && ( - WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (node) || - WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node)); -} - -gboolean -node_is_list_or_item (WebKitDOMNode *node) -{ - return node && (node_is_list (node) || WEBKIT_DOM_IS_HTMLLI_ELEMENT (node)); -} -/** - * get_list_format_from_node: - * @node: an #WebKitDOMNode - * - * Returns block format of given list. - * - * Returns: #EHTMLEditorSelectionBlockFormat - */ -EHTMLEditorSelectionBlockFormat -get_list_format_from_node (WebKitDOMNode *node) -{ - EHTMLEditorSelectionBlockFormat format = - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST; - - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node)) - return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE; - - if (WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node)) - return format; - - if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (node)) { - gchar *type_value = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (node), "type"); - - if (!type_value) - return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST; - - if (!*type_value) - format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST; - else if (g_ascii_strcasecmp (type_value, "A") == 0) - format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA; - else if (g_ascii_strcasecmp (type_value, "I") == 0) - format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN; - g_free (type_value); - - return format; - } - - return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE; -} - -void -merge_list_into_list (WebKitDOMNode *from, - WebKitDOMNode *to, - gboolean insert_before) -{ - WebKitDOMNode *item, *insert_before_node; - - if (!(to && from)) - return; - - insert_before_node = webkit_dom_node_get_first_child (to); - while ((item = webkit_dom_node_get_first_child (from)) != NULL) { - if (insert_before) - webkit_dom_node_insert_before ( - to, item, insert_before_node, NULL); - else - webkit_dom_node_append_child (to, item, NULL); - } - - if (!webkit_dom_node_get_first_child (from)) - remove_node (from); -} - -void -merge_lists_if_possible (WebKitDOMNode *list) -{ - EHTMLEditorSelectionBlockFormat format, prev, next; - gint ii, length; - WebKitDOMNode *prev_sibling, *next_sibling; - WebKitDOMNodeList *lists; - - prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (list)); - next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (list)); - - format = get_list_format_from_node (list), - prev = get_list_format_from_node (prev_sibling); - next = get_list_format_from_node (next_sibling); - - if (format != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE) { - if (format == prev && prev != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE) - merge_list_into_list (prev_sibling, list, TRUE); - - if (format == next && next != E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE) - merge_list_into_list (next_sibling, list, FALSE); - } - - lists = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (list), "ol + ol, ul + ul", NULL); - length = webkit_dom_node_list_get_length (lists); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (lists, ii); - merge_lists_if_possible (node); - g_object_unref (node); - } - g_object_unref (lists); -} - -WebKitDOMElement * -get_parent_block_element (WebKitDOMNode *node) -{ - WebKitDOMElement *parent = webkit_dom_node_get_parent_element (node); - - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) - return WEBKIT_DOM_ELEMENT (node); - - while (parent && - !WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) && - !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && - !WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (parent) && - !WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (parent) && - !WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent) && - !WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (parent) && - !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent) && - !element_has_tag (parent, "address")) { - parent = webkit_dom_node_get_parent_element ( - WEBKIT_DOM_NODE (parent)); - } - - return parent; -} - -void -dom_element_rename_attribute (WebKitDOMElement *element, - const gchar *from, - const gchar *to) -{ - gchar *value; - - if (!webkit_dom_element_has_attribute (element, from)) - return; - - value = webkit_dom_element_get_attribute (element, from); - webkit_dom_element_set_attribute (element, to, (value && *value) ? value : "", NULL); - webkit_dom_element_remove_attribute (element, from); - g_free (value); -} - -void -dom_element_swap_attributes (WebKitDOMElement *element, - const gchar *from, - const gchar *to) -{ - gchar *value_from, *value_to; - - if (!webkit_dom_element_has_attribute (element, from) || - !webkit_dom_element_has_attribute (element, to)) - return; - - value_from = webkit_dom_element_get_attribute (element, from); - value_to = webkit_dom_element_get_attribute (element, to); - webkit_dom_element_set_attribute (element, to, (value_from && *value_from) ? value_from : "", NULL); - webkit_dom_element_set_attribute (element, from, (value_to && *value_to) ? value_to : "", NULL); - g_free (value_from); - g_free (value_to); -} diff --git a/e-util/e-html-editor-utils.h b/e-util/e-html-editor-utils.h deleted file mode 100644 index 64da046..0000000 --- a/e-util/e-html-editor-utils.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * e-html-editor-utils.h - * - * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com> - * - * 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 <http://www.gnu.org/licenses/> - * - */ - -#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION) -#error "Only <e-util/e-util.h> should be included directly." -#endif - -#ifndef E_HTML_EDITOR_UTILS_H -#define E_HTML_EDITOR_UTILS_H - -#include <e-util/e-util-enums.h> - -#include <webkit/webkitdom.h> - -G_BEGIN_DECLS - -WebKitDOMElement * - e_html_editor_dom_node_find_parent_element - (WebKitDOMNode *node, - const gchar *tagname); - -WebKitDOMElement * - e_html_editor_dom_node_find_child_element - (WebKitDOMNode *node, - const gchar *tagname); - -gboolean e_html_editor_node_is_selection_position_node - (WebKitDOMNode *node); - -WebKitDOMNode * e_html_editor_get_parent_block_node_from_child - (WebKitDOMNode *node); - -gboolean element_has_id (WebKitDOMElement *element, - const gchar* id); - -gboolean element_has_tag (WebKitDOMElement *element, - const gchar* tag); - -gboolean element_has_class (WebKitDOMElement *element, - const gchar* class); - -void element_add_class (WebKitDOMElement *element, - const gchar* class); - -void element_remove_class (WebKitDOMElement *element, - const gchar* class); - -void remove_node (WebKitDOMNode *node); - -void remove_node_if_empty (WebKitDOMNode *node); - -WebKitDOMNode * split_node_into_two (WebKitDOMNode *item, - gint level); - -WebKitDOMElement * - create_selection_marker (WebKitDOMDocument *document, - gboolean start); - -void add_selection_markers_into_element_start - (WebKitDOMDocument *document, - WebKitDOMElement *element, - WebKitDOMElement **selection_start_marker, - WebKitDOMElement **selection_end_marker); - -void add_selection_markers_into_element_end - (WebKitDOMDocument *document, - WebKitDOMElement *element, - WebKitDOMElement **selection_start_marker, - WebKitDOMElement **selection_end_marker); - -void remove_selection_markers (WebKitDOMDocument *document); - -gboolean node_is_list (WebKitDOMNode *node); - -gboolean node_is_list_or_item (WebKitDOMNode *node); - -EHTMLEditorSelectionBlockFormat - get_list_format_from_node (WebKitDOMNode *node); - -void merge_list_into_list (WebKitDOMNode *from, - WebKitDOMNode *to, - gboolean insert_before); - -void merge_lists_if_possible (WebKitDOMNode *list); - -WebKitDOMElement * - get_parent_block_element (WebKitDOMNode *node); - -void dom_element_rename_attribute (WebKitDOMElement *element, - const gchar *from, - const gchar *to); - -void dom_element_swap_attributes (WebKitDOMElement *element, - const gchar *from, - const gchar *to); -G_END_DECLS - -#endif /* E_HTML_EDITOR_UTILS_H */ diff --git a/e-util/e-html-editor-view.c b/e-util/e-html-editor-view.c deleted file mode 100644 index 9770887..0000000 --- a/e-util/e-html-editor-view.c +++ /dev/null @@ -1,15621 +0,0 @@ -/* - * e-html-editor-view.c - * - * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com> - * - * 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 <http://www.gnu.org/licenses/> - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "e-html-editor-view.h" -#include "e-html-editor.h" -#include "e-emoticon-chooser.h" - -#include <e-util/e-util.h> -#include <e-util/e-marshal.h> -#include <glib/gi18n-lib.h> -#include <gdk/gdkkeysyms.h> - -#define E_HTML_EDITOR_VIEW_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewPrivate)) - -/* stephenhay from https://mathiasbynens.be/demo/url-regex */ -#define URL_PROTOCOLS "news|telnet|nntp|file|https?|s?ftp|webcal|localhost|ssh" -#define URL_PATTERN_BASE "(?=((?:(?:(?:" URL_PROTOCOLS ")\\:\\/\\/)|(?:www\\.|ftp\\.))[^\\s\\/\\$\\.\\?#].[^\\s]*)" -#define URL_PATTERN_NO_NBSP ")((?:(?! ).)*)" -#define URL_PATTERN URL_PATTERN_BASE URL_PATTERN_NO_NBSP -#define URL_PATTERN_SPACE URL_PATTERN_BASE "\\s$" URL_PATTERN_NO_NBSP -/* Taken from camel-url-scanner.c */ -#define URL_INVALID_TRAILING_CHARS ",.:;?!-|}])\"" - -/* http://www.w3.org/TR/html5/forms.html#valid-e-mail-address */ -#define E_MAIL_PATTERN \ - "[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}"\ - "[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*" - -#define E_MAIL_PATTERN_SPACE E_MAIL_PATTERN "\\s" - -#define QUOTE_SYMBOL ">" - -#define HTML_KEY_CODE_BACKSPACE 8 -#define HTML_KEY_CODE_RETURN 13 -#define HTML_KEY_CODE_CONTROL 17 -#define HTML_KEY_CODE_SPACE 32 -#define HTML_KEY_CODE_DELETE 46 - -#define HISTORY_SIZE_LIMIT 30 - -#define d(x) - -/** - * EHTMLEditorView: - * - * The #EHTMLEditorView is a WebKit-based rich text editor. The view itself - * only provides means to configure global behavior of the editor. To work - * with the actual content, current cursor position or current selection, - * use #EHTMLEditorSelection object. - */ - -struct _EHTMLEditorViewPrivate { - gint changed : 1; - gint inline_spelling : 1; - gint magic_links : 1; - gint magic_smileys : 1; - gint unicode_smileys : 1; - gint can_copy : 1; - gint can_cut : 1; - gint can_paste : 1; - gint can_redo : 1; - gint can_undo : 1; - gint reload_in_progress : 1; - gint html_mode : 1; - - EHTMLEditorSelection *selection; - - GHashTable *inline_images; - - GSettings *mail_settings; - GSettings *font_settings; - GSettings *aliasing_settings; - - gboolean convert_in_situ; - gboolean body_input_event_removed; - gboolean is_editting_message; - gboolean is_message_from_draft; - gboolean is_message_from_edit_as_new; - gboolean is_new_message; - gboolean is_message_from_selection; - gboolean return_key_pressed; - gboolean space_key_pressed; - gboolean smiley_written; - gboolean undo_redo_in_progress; - gboolean dont_save_history_in_body_input; - gboolean composition_in_progress; - gboolean style_change_callbacks_blocked; - gboolean selection_changed_callbacks_blocked; - gboolean copy_paste_clipboard_in_view; - gboolean copy_paste_primary_in_view; - gboolean copy_action_triggered; - gboolean pasting_primary_clipboard; - gboolean renew_history_after_coordinates; - - GHashTable *old_settings; - - GQueue *post_reload_operations; - guint spell_check_on_scroll_event_source_id; - - gulong owner_change_primary_cb_id; - gulong owner_change_clipboard_cb_id; - - GList *history; - guint history_size; -}; - -enum { - PROP_0, - PROP_CAN_COPY, - PROP_CAN_CUT, - PROP_CAN_PASTE, - PROP_CAN_REDO, - PROP_CAN_UNDO, - PROP_CHANGED, - PROP_HTML_MODE, - PROP_INLINE_SPELLING, - PROP_MAGIC_LINKS, - PROP_MAGIC_SMILEYS, - PROP_UNICODE_SMILEYS, - PROP_SPELL_CHECKER -}; - -enum { - POPUP_EVENT, - PASTE_PRIMARY_CLIPBOARD, - IS_READY, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -static CamelDataCache *emd_global_http_cache = NULL; - -typedef void (*PostReloadOperationFunc) (EHTMLEditorView *view, gpointer data); - -typedef struct { - PostReloadOperationFunc func; - gpointer data; - GDestroyNotify data_free_func; -} PostReloadOperation; - -G_DEFINE_TYPE_WITH_CODE ( - EHTMLEditorView, - e_html_editor_view, - WEBKIT_TYPE_WEB_VIEW, - G_IMPLEMENT_INTERFACE ( - E_TYPE_EXTENSIBLE, NULL)) - -static void -html_editor_view_queue_post_reload_operation (EHTMLEditorView *view, - PostReloadOperationFunc func, - gpointer data, - GDestroyNotify data_free_func) -{ - PostReloadOperation *op; - - g_return_if_fail (func != NULL); - - if (view->priv->post_reload_operations == NULL) - view->priv->post_reload_operations = g_queue_new (); - - op = g_new0 (PostReloadOperation, 1); - op->func = func; - op->data = data; - op->data_free_func = data_free_func; - - g_queue_push_head (view->priv->post_reload_operations, op); -} - -static WebKitDOMRange * -html_editor_view_get_dom_range (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { - g_object_unref (dom_selection); - return NULL; - } - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - g_object_unref (dom_selection); - return range; -} - -static gchar * -get_node_inner_html (WebKitDOMNode *node) -{ - gchar *inner_html; - WebKitDOMDocument *document; - WebKitDOMElement *div; - - document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (node)); - div = webkit_dom_document_create_element (document, "div", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (div), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (node), TRUE), - NULL); - - inner_html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (div)); - remove_node (WEBKIT_DOM_NODE (div)); - - return inner_html; -} - -#if d(1)+0 -static void -print_node_inner_html (WebKitDOMNode *node) -{ - gchar *inner_html; - - if (!node) { - printf (" none\n"); - return; - } - - inner_html = get_node_inner_html (node); - - printf (" '%s'\n", inner_html); - - g_free (inner_html); -} - -static void -print_history_event (EHTMLEditorViewHistoryEvent *event) -{ - if (event->type != HISTORY_START && event->type != HISTORY_AND) { - printf (" HISTORY EVENT: %d ; \n", event->type); - printf (" before: start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n", - event->before.start.x, event->before.start.y, event->before.end.x, event->before.end.y); - printf (" after: start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n", - event->after.start.x, event->after.start.y, event->after.end.x, event->after.end.y); - } - switch (event->type) { - case HISTORY_DELETE: - case HISTORY_INPUT: - case HISTORY_REMOVE_LINK: - case HISTORY_SMILEY: - case HISTORY_IMAGE: - case HISTORY_CITATION_SPLIT: - case HISTORY_BLOCKQUOTE: - print_node_inner_html (WEBKIT_DOM_NODE (event->data.fragment)); - break; - case HISTORY_ALIGNMENT: - case HISTORY_BLOCK_FORMAT: - case HISTORY_BOLD: - case HISTORY_FONT_SIZE: - case HISTORY_INDENT: - case HISTORY_ITALIC: - case HISTORY_MONOSPACE: - case HISTORY_UNDERLINE: - case HISTORY_STRIKETHROUGH: - case HISTORY_WRAP: - printf (" from %d to %d ;\n", event->data.style.from, event->data.style.to); - break; - case HISTORY_PASTE: - case HISTORY_PASTE_AS_TEXT: - case HISTORY_PASTE_QUOTED: - case HISTORY_INSERT_HTML: - printf (" pasting: '%s' ; \n", event->data.string.to); - break; - case HISTORY_HRULE_DIALOG: - case HISTORY_IMAGE_DIALOG: - case HISTORY_LINK_DIALOG: - case HISTORY_CELL_DIALOG: - case HISTORY_TABLE_DIALOG: - case HISTORY_TABLE_INPUT: - case HISTORY_PAGE_DIALOG: - case HISTORY_UNQUOTE: - print_node_inner_html (event->data.dom.from); - print_node_inner_html (event->data.dom.to); - break; - case HISTORY_FONT_COLOR: - case HISTORY_REPLACE: - case HISTORY_REPLACE_ALL: - printf (" from '%s' to '%s';\n", event->data.string.from, event->data.string.to); - break; - case HISTORY_START: - printf (" HISTORY START\n"); - break; - case HISTORY_AND: - printf (" HISTORY AND\n"); - break; - default: - printf (" UNKNOWN HISTORY TYPE\n"); - } -} - -static void -print_history (EHTMLEditorView *view) -{ - printf ("-------------------\nWHOLE HISTORY STACK\n"); - if (view->priv->history) { - g_list_foreach ( - view->priv->history, - (GFunc) print_history_event, - NULL); - } - - printf ("-------------------\n"); -} - -static void -print_undo_events (EHTMLEditorView *view) -{ - GList *item = view->priv->history; - - printf ("------------------\nUNDO HISTORY STACK\n"); - if (!item || !item->next) { - printf ("------------------\n"); - return; - } - - print_history_event (item->data); - item = item->next; - while (item) { - print_history_event (item->data); - item = item->next; - } - - printf ("------------------\n"); - -} - -static void -print_redo_events (EHTMLEditorView *view) -{ - GList *item = view->priv->history; - - printf ("------------------\nREDO HISTORY STACK\n"); - if (!item || !item->prev) { - printf ("------------------\n"); - return; - } - - item = item->prev; - while (item) { - print_history_event (item->data); - item = item->prev; - } - - printf ("------------------\n"); -} -#endif - -gboolean -e_html_editor_view_can_redo (EHTMLEditorView *view) -{ - if (view->priv->history && view->priv->history->prev) - return TRUE; - else - return FALSE; -} - -gboolean -e_html_editor_view_can_undo (EHTMLEditorView *view) -{ - if (view->priv->history) { - EHTMLEditorViewHistoryEvent *event; - - event = view->priv->history->data; - - return (event->type != HISTORY_START); - } else - return FALSE; -} - -static void -html_editor_view_user_changed_contents_cb (EHTMLEditorView *view) -{ - gboolean can_redo, can_undo; - - e_html_editor_view_set_changed (view, TRUE); - - can_redo = e_html_editor_view_can_redo (view); - if (view->priv->can_redo != can_redo) { - view->priv->can_redo = can_redo; - g_object_notify (G_OBJECT (view), "can-redo"); - } - - can_undo = e_html_editor_view_can_undo (view); - if (view->priv->can_undo != can_undo) { - view->priv->can_undo = can_undo; - g_object_notify (G_OBJECT (view), "can-undo"); - } -} - -static void -html_editor_view_selection_changed_cb (EHTMLEditorView *view, - gpointer user_data) -{ - WebKitWebView *web_view; - gboolean can_copy, can_cut, can_paste; - - web_view = WEBKIT_WEB_VIEW (view); - - /* When the webview is being (re)loaded, the document is in an - * inconsistant state and there is no selection, so don't propagate - * the signal further to EHTMLEditorSelection and others and wait until - * the load is finished. */ - if (view->priv->reload_in_progress) { - g_signal_stop_emission_by_name (view, "selection-changed"); - return; - } - - can_copy = webkit_web_view_can_copy_clipboard (web_view); - if (view->priv->can_copy != can_copy) { - view->priv->can_copy = can_copy; - /* This means that we have an active selection thus the primary - * clipboard content is from composer. */ - if (can_copy) - view->priv->copy_paste_primary_in_view = TRUE; - g_object_notify (G_OBJECT (view), "can-copy"); - } - - can_cut = webkit_web_view_can_cut_clipboard (web_view); - if (view->priv->can_cut != can_cut) { - view->priv->can_cut = can_cut; - g_object_notify (G_OBJECT (view), "can-cut"); - } - - can_paste = webkit_web_view_can_paste_clipboard (web_view); - if (view->priv->can_paste != can_paste) { - view->priv->can_paste = can_paste; - g_object_notify (G_OBJECT (view), "can-paste"); - } -} - -static void -block_selection_changed_callbacks (EHTMLEditorView *view) -{ - e_html_editor_selection_block_selection_changed (view->priv->selection); - if (!view->priv->selection_changed_callbacks_blocked) { - g_signal_handlers_block_by_func (view, html_editor_view_selection_changed_cb, NULL); - view->priv->selection_changed_callbacks_blocked = TRUE; - } -} - -static void -unblock_selection_changed_callbacks (EHTMLEditorView *view) -{ - e_html_editor_selection_unblock_selection_changed (view->priv->selection); - if (view->priv->selection_changed_callbacks_blocked) { - g_signal_handlers_unblock_by_func (view, html_editor_view_selection_changed_cb, NULL); - view->priv->selection_changed_callbacks_blocked = FALSE; - } -} - -static gboolean -html_editor_view_should_show_delete_interface_for_element (EHTMLEditorView *view, - WebKitDOMHTMLElement *element) -{ - return FALSE; -} - -static void -perform_spell_check (WebKitDOMDOMSelection *dom_selection, - WebKitDOMRange *start_range, - WebKitDOMRange *end_range) -{ - WebKitDOMRange *actual = start_range; - - /* Go through all words to spellcheck them. To avoid this we have to wait for - * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */ - /* We are moving forward word by word until we hit the text on the end. */ - while (actual && webkit_dom_range_compare_boundary_points (end_range, 2 /* END_TO_END */, actual, NULL) != 0) { - g_object_unref (actual); - webkit_dom_dom_selection_modify ( - dom_selection, "move", "forward", "word"); - actual = webkit_dom_dom_selection_get_range_at ( - dom_selection, 0, NULL); - } - g_clear_object (&actual); -} - -void -e_html_editor_view_force_spell_check_for_current_paragraph (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMDOMWindow *dom_window; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMElement *parent, *element; - WebKitDOMRange *end_range, *actual; - WebKitDOMText *text; - - if (!view->priv->inline_spelling) - return; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - element = webkit_dom_document_query_selector ( - document, "body[spellcheck=true]", NULL); - - if (!element) - return; - - if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element))) - return; - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - if (!selection_start_marker || !selection_end_marker) - return; - - /* Block callbacks of selection-changed signal as we don't want to - * recount all the block format things in EHTMLEditorSelection and here as well - * when we are moving with caret */ - block_selection_changed_callbacks (view); - - parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_end_marker)); - - /* Append some text on the end of the element */ - text = webkit_dom_document_create_text_node (document, "-x-evo-end"); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (parent), - WEBKIT_DOM_NODE (text), - NULL); - - parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker)); - - /* Create range that's pointing on the end of this text */ - end_range = webkit_dom_document_create_range (document); - webkit_dom_range_select_node_contents ( - end_range, WEBKIT_DOM_NODE (text), NULL); - webkit_dom_range_collapse (end_range, FALSE, NULL); - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - /* Move on the beginning of the paragraph */ - actual = webkit_dom_document_create_range (document); - webkit_dom_range_select_node_contents ( - actual, WEBKIT_DOM_NODE (parent), NULL); - webkit_dom_range_collapse (actual, TRUE, NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, actual); - - actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - perform_spell_check (dom_selection, actual, end_range); - - g_object_unref (dom_selection); - g_object_unref (dom_window); - g_object_unref (end_range); - - /* Remove the text that we inserted on the end of the paragraph */ - remove_node (WEBKIT_DOM_NODE (text)); - - /* Unblock the callbacks */ - unblock_selection_changed_callbacks (view); - - e_html_editor_selection_restore (selection); -} - -static void -refresh_spell_check (EHTMLEditorView *view, - gboolean enable_spell_check) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMDOMWindow *dom_window; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMHTMLElement *body; - WebKitDOMRange *end_range, *actual; - WebKitDOMText *text; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))) - return; - - /* Enable/Disable spellcheck in composer */ - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), - "spellcheck", - enable_spell_check ? "true" : "false", - NULL); - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - /* Sometimes the web view is not focused, so we have to save the selection - * manually into the body */ - if (!selection_start_marker || !selection_end_marker) { - WebKitDOMNode *child; - - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - if (!WEBKIT_DOM_IS_HTML_ELEMENT (child)) - return; - - add_selection_markers_into_element_start ( - document, - WEBKIT_DOM_ELEMENT (child), - &selection_start_marker, - &selection_end_marker); - } - - /* Block callbacks of selection-changed signal as we don't want to - * recount all the block format things in EHTMLEditorSelection and here as well - * when we are moving with caret */ - block_selection_changed_callbacks (view); - - /* Append some text on the end of the body */ - text = webkit_dom_document_create_text_node (document, "-x-evo-end"); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL); - - /* Create range that's pointing on the end of this text */ - end_range = webkit_dom_document_create_range (document); - webkit_dom_range_select_node_contents ( - end_range, WEBKIT_DOM_NODE (text), NULL); - webkit_dom_range_collapse (end_range, FALSE, NULL); - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - /* Move on the beginning of the document */ - webkit_dom_dom_selection_modify ( - dom_selection, "move", "backward", "documentboundary"); - - actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - perform_spell_check (dom_selection, actual, end_range); - - g_object_unref (dom_selection); - g_object_unref (dom_window); - g_object_unref (end_range); - - /* Remove the text that we inserted on the end of the body */ - remove_node (WEBKIT_DOM_NODE (text)); - - /* Unblock the callbacks */ - unblock_selection_changed_callbacks (view); - - e_html_editor_selection_restore (selection); -} - -void -e_html_editor_view_turn_spell_check_off (EHTMLEditorView *view) -{ - refresh_spell_check (view, FALSE); -} - -void -e_html_editor_view_force_spell_check_in_viewport (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - glong viewport_height; - WebKitDOMDocument *document; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMDOMWindow *dom_window; - WebKitDOMElement *last_element; - WebKitDOMHTMLElement *body; - WebKitDOMRange *end_range, *actual; - WebKitDOMText *text; - - if (!view->priv->inline_spelling) - return; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = WEBKIT_DOM_HTML_ELEMENT (webkit_dom_document_query_selector ( - document, "body[spellcheck=true]", NULL)); - - if (!body) { - body = webkit_dom_document_get_body (document); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), "spellcheck", "true", NULL); - } - - if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))) - return; - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_save (selection); - - /* Block callbacks of selection-changed signal as we don't want to - * recount all the block format things in EHTMLEditorSelection and here as well - * when we are moving with caret */ - block_selection_changed_callbacks (view); - - /* We have to add 10 px offset as otherwise just the HTML element will be returned */ - actual = webkit_dom_document_caret_range_from_point (document, 10, 10); - if (!actual) - return; - - /* Append some text on the end of the body */ - text = webkit_dom_document_create_text_node (document, "-x-evo-end"); - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - /* We have to add 10 px offset as otherwise just the HTML element will be returned */ - viewport_height = webkit_dom_dom_window_get_inner_height (dom_window); - last_element = webkit_dom_document_element_from_point (document, 10, viewport_height - 10); - if (last_element && !WEBKIT_DOM_IS_HTML_HTML_ELEMENT (last_element) && - !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (last_element)) { - WebKitDOMElement *parent; - - parent = get_parent_block_element (WEBKIT_DOM_NODE (last_element)); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (parent), WEBKIT_DOM_NODE (text), NULL); - } else - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL); - - /* Create range that's pointing on the end of viewport */ - end_range = webkit_dom_document_create_range (document); - webkit_dom_range_select_node_contents ( - end_range, WEBKIT_DOM_NODE (text), NULL); - webkit_dom_range_collapse (end_range, FALSE, NULL); - - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, actual); - perform_spell_check (dom_selection, actual, end_range); - - g_object_unref (dom_selection); - g_object_unref (dom_window); - g_object_unref (end_range); - - /* Remove the text that we inserted on the end of the body */ - remove_node (WEBKIT_DOM_NODE (text)); - - /* Unblock the callbacks */ - unblock_selection_changed_callbacks (view); - - e_html_editor_selection_restore (selection); -} - -void -e_html_editor_view_force_spell_check (EHTMLEditorView *view) -{ - if (view->priv->inline_spelling) - refresh_spell_check (view, TRUE); -} - -static gint -get_citation_level (WebKitDOMNode *node, - gboolean set_plaintext_quoted) -{ - WebKitDOMNode *parent = node; - gint level = 0; - - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && - webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type")) { - level++; - - if (set_plaintext_quoted) { - element_add_class ( - WEBKIT_DOM_ELEMENT (parent), - "-x-evo-plaintext-quoted"); - } - } - - parent = webkit_dom_node_get_parent_node (parent); - } - - return level; -} - -static gchar * -get_quotation_for_level (gint quote_level) -{ - gint ii; - GString *output = g_string_new (""); - - for (ii = 0; ii < quote_level; ii++) { - g_string_append (output, "<span class=\"-x-evo-quote-character\">"); - g_string_append (output, QUOTE_SYMBOL); - g_string_append (output, " "); - g_string_append (output, "</span>"); - } - - return g_string_free (output, FALSE); -} - -void -e_html_editor_view_quote_plain_text_element_after_wrapping (WebKitDOMDocument *document, - WebKitDOMElement *element, - gint quote_level) -{ - WebKitDOMNodeList *list; - WebKitDOMNode *quoted_node; - gint length, ii; - gchar *quotation; - - quoted_node = WEBKIT_DOM_NODE ( - webkit_dom_document_create_element (document, "SPAN", NULL)); - webkit_dom_element_set_class_name ( - WEBKIT_DOM_ELEMENT (quoted_node), "-x-evo-quoted"); - quotation = get_quotation_for_level (quote_level); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (quoted_node), quotation, NULL); - - list = webkit_dom_element_query_selector_all ( - element, "br.-x-evo-wrap-br", NULL); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (element), - quoted_node, - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)), - NULL); - - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *br = webkit_dom_node_list_item (list, ii); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (br), - webkit_dom_node_clone_node (quoted_node, TRUE), - webkit_dom_node_get_next_sibling (br), - NULL); - g_object_unref (br); - } - - g_object_unref (list); - g_free (quotation); -} - -static gboolean -is_citation_node (WebKitDOMNode *node) -{ - gchar *value; - - if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) - return FALSE; - - value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type"); - - /* citation == <blockquote type='cite'> */ - if (g_strcmp0 (value, "cite") == 0) { - g_free (value); - return TRUE; - } else { - g_free (value); - return FALSE; - } -} - -static gboolean -return_pressed_in_empty_line (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - WebKitDOMDOMSelection *dom_selection; - WebKitDOMDOMWindow *dom_window; - WebKitDOMNode *node; - WebKitDOMRange *range; - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - if (!range) { - g_object_unref (dom_selection); - return FALSE; - } - - g_object_unref (dom_selection); - - node = webkit_dom_range_get_start_container (range, NULL); - if (!WEBKIT_DOM_IS_TEXT (node)) { - WebKitDOMNode *first_child; - - first_child = webkit_dom_node_get_first_child (node); - if (first_child && WEBKIT_DOM_IS_ELEMENT (first_child) && - element_has_class (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-quoted")) { - WebKitDOMNode *prev_sibling; - - prev_sibling = webkit_dom_node_get_previous_sibling (node); - if (!prev_sibling) { - gboolean collapsed; - - collapsed = webkit_dom_range_get_collapsed (range, NULL); - g_object_unref (range); - return collapsed; - } - } - } - - g_object_unref (range); - - return FALSE; -} - -static WebKitDOMElement * -prepare_paragraph (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - gboolean with_selection) -{ - WebKitDOMElement *element, *paragraph; - - paragraph = e_html_editor_selection_get_paragraph_element ( - selection, document, -1, 0); - - if (with_selection) - add_selection_markers_into_element_start ( - document, paragraph, NULL, NULL); - - element = webkit_dom_document_create_element (document, "BR", NULL); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (paragraph), WEBKIT_DOM_NODE (element), NULL); - - return paragraph; -} - -static WebKitDOMElement * -wrap_and_quote_element (EHTMLEditorView *view, - WebKitDOMElement *element) -{ - gint citation_level; - WebKitDOMElement *tmp_element = element; - - g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (element), element); - - if (view->priv->html_mode) - return element; - - citation_level = get_citation_level (WEBKIT_DOM_NODE (element), FALSE); - - remove_quoting_from_element (element); - remove_wrapping_from_element (element); - - if (element_has_class (element, "-x-evo-paragraph")) { - gint word_wrap_length, length; - - word_wrap_length = e_html_editor_selection_get_word_wrap_length ( - view->priv->selection); - length = word_wrap_length - 2 * citation_level; - tmp_element = e_html_editor_selection_wrap_paragraph_length ( - view->priv->selection, element, length); - } - - if (citation_level > 0) { - WebKitDOMDocument *document; - - webkit_dom_node_normalize (WEBKIT_DOM_NODE (tmp_element)); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, tmp_element, citation_level); - } - - return tmp_element; -} - -static WebKitDOMElement * -insert_new_line_into_citation (EHTMLEditorView *view, - const gchar *html_to_insert) -{ - gboolean html_mode, ret_val, avoid_editor_call; - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *element, *paragraph = NULL; - WebKitDOMNode *last_block; - - html_mode = e_html_editor_view_get_html_mode (view); - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - avoid_editor_call = - return_pressed_in_empty_line (selection, document); - - if (avoid_editor_call) { - WebKitDOMElement *selection_start_marker; - WebKitDOMNode *current_block, *parent, *parent_block, *block_clone; - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - current_block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - block_clone = webkit_dom_node_clone_node (current_block, TRUE); - /* Find selection start marker and restore it after the new line - * is inserted */ - selection_start_marker = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block_clone), "#-x-evo-selection-start-marker", NULL); - - /* Find parent node that is immediate child of the BODY */ - /* Build the same structure of parent nodes of the current block */ - parent_block = current_block; - parent = webkit_dom_node_get_parent_node (parent_block); - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - WebKitDOMNode *node; - - parent_block = parent; - node = webkit_dom_node_clone_node (parent_block, FALSE); - webkit_dom_node_append_child (node, block_clone, NULL); - block_clone = node; - parent = webkit_dom_node_get_parent_node (parent_block); - } - - paragraph = e_html_editor_selection_get_paragraph_element ( - selection, document, -1, 0); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (paragraph), - WEBKIT_DOM_NODE ( - webkit_dom_document_create_element (document, "BR", NULL)), - NULL); - - /* Insert the selection markers to right place */ - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (paragraph), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker)), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph)), - NULL); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (paragraph), - WEBKIT_DOM_NODE (selection_start_marker), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph)), - NULL); - - /* Insert the cloned nodes before the BODY parent node */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent_block), - block_clone, - parent_block, - NULL); - - /* Insert the new empty paragraph before the BODY parent node */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent_block), - WEBKIT_DOM_NODE (paragraph), - parent_block, - NULL); - - /* Remove the old block (its copy was moved to the right place) */ - remove_node (current_block); - - e_html_editor_selection_restore (selection); - - return NULL; - } else { - e_html_editor_view_remove_input_event_listener_from_body (view); - block_selection_changed_callbacks (view); - - ret_val = e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL); - - unblock_selection_changed_callbacks (view); - e_html_editor_view_register_input_event_listener_on_body (view); - - if (!ret_val) - return NULL; - - element = webkit_dom_document_query_selector ( - document, "body>br", NULL); - - if (!element) - return NULL; - } - - last_block = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - while (last_block && is_citation_node (last_block)) - last_block = webkit_dom_node_get_last_child (last_block); - - if (last_block) { - WebKitDOMNode *last_child; - - if ((last_child = webkit_dom_node_get_last_child (last_block))) { - if (WEBKIT_DOM_IS_ELEMENT (last_child) && - element_has_class (WEBKIT_DOM_ELEMENT (last_child), "-x-evo-quoted")) - webkit_dom_node_append_child ( - last_block, - WEBKIT_DOM_NODE ( - webkit_dom_document_create_element ( - document, "br", NULL)), - NULL); - } - } - - if (!html_mode) { - WebKitDOMNode *sibling; - - sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - - if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (sibling)) { - WebKitDOMNode *node; - - node = webkit_dom_node_get_first_child (sibling); - while (node && is_citation_node (node)) - node = webkit_dom_node_get_first_child (node); - - /* Rewrap and requote nodes that were created by split. */ - if (WEBKIT_DOM_IS_ELEMENT (node)) - wrap_and_quote_element (view, WEBKIT_DOM_ELEMENT (node)); - - if (WEBKIT_DOM_IS_ELEMENT (last_block)) - wrap_and_quote_element (view, WEBKIT_DOM_ELEMENT (last_block)); - - e_html_editor_view_force_spell_check_in_viewport (view); - } - } - - if (html_to_insert && *html_to_insert) { - paragraph = prepare_paragraph (selection, document, FALSE); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (paragraph), - html_to_insert, - NULL); - - if (!webkit_dom_element_query_selector (paragraph, "#-x-evo-selection-start-marker", NULL)) - add_selection_markers_into_element_end ( - document, paragraph, NULL, NULL); - } else - paragraph = prepare_paragraph (selection, document, TRUE); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - WEBKIT_DOM_NODE (paragraph), - WEBKIT_DOM_NODE (element), - NULL); - - remove_node (WEBKIT_DOM_NODE (element)); - - e_html_editor_selection_restore (selection); - - return paragraph; -} - -static void -set_base64_to_element_attribute (EHTMLEditorView *view, - WebKitDOMElement *element, - const gchar *attribute) -{ - gchar *attribute_value; - const gchar *base64_src; - - attribute_value = webkit_dom_element_get_attribute (element, attribute); - - if (attribute_value && (base64_src = g_hash_table_lookup (view->priv->inline_images, attribute_value)) != NULL) { - const gchar *base64_data = strstr (base64_src, ";") + 1; - gchar *name; - glong name_length; - - name_length = - g_utf8_strlen (base64_src, -1) - - g_utf8_strlen (base64_data, -1) - 1; - name = g_strndup (base64_src, name_length); - - webkit_dom_element_set_attribute (element, "data-inline", "", NULL); - webkit_dom_element_set_attribute (element, "data-name", name, NULL); - webkit_dom_element_set_attribute (element, attribute, base64_data, NULL); - - g_free (name); - } - - g_free (attribute_value); -} - -static void -change_cid_images_src_to_base64 (EHTMLEditorView *view) -{ - gint ii, length; - WebKitDOMDocument *document; - WebKitDOMElement *document_element; - WebKitDOMNamedNodeMap *attributes; - WebKitDOMNodeList *list; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - document_element = webkit_dom_document_get_document_element (document); - - list = webkit_dom_document_query_selector_all (document, "img[src^=\"cid:\"]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - set_base64_to_element_attribute (view, WEBKIT_DOM_ELEMENT (node), "src"); - g_object_unref (node); - } - g_object_unref (list); - - /* Namespaces */ - attributes = webkit_dom_element_get_attributes (document_element); - length = webkit_dom_named_node_map_get_length (attributes); - for (ii = 0; ii < length; ii++) { - gchar *name; - WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); - - name = webkit_dom_node_get_local_name (node); - - if (g_str_has_prefix (name, "xmlns:")) { - const gchar *ns = name + 6; - gchar *attribute_ns = g_strconcat (ns, ":src", NULL); - gchar *selector = g_strconcat ("img[", ns, "\\:src^=\"cid:\"]", NULL); - gint ns_length, jj; - - list = webkit_dom_document_query_selector_all ( - document, selector, NULL); - ns_length = webkit_dom_node_list_get_length (list); - for (jj = 0; jj < ns_length; jj++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, jj); - - set_base64_to_element_attribute ( - view, WEBKIT_DOM_ELEMENT (node), attribute_ns); - g_object_unref (node); - } - - g_object_unref (list); - g_free (attribute_ns); - g_free (selector); - } - g_object_unref (node); - g_free (name); - } - g_object_unref (attributes); - - list = webkit_dom_document_query_selector_all ( - document, "[background^=\"cid:\"]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - set_base64_to_element_attribute ( - view, WEBKIT_DOM_ELEMENT (node), "background"); - g_object_unref (node); - } - g_object_unref (list); - g_hash_table_remove_all (view->priv->inline_images); -} - -/* For purpose of this function see e-mail-formatter-quote.c */ -static void -put_body_in_citation (WebKitDOMDocument *document) -{ - WebKitDOMElement *cite_body = webkit_dom_document_query_selector ( - document, "span.-x-evo-cite-body", NULL); - - if (cite_body) { - WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document); - WebKitDOMNode *citation; - WebKitDOMNode *sibling; - - citation = WEBKIT_DOM_NODE ( - webkit_dom_document_create_element (document, "blockquote", NULL)); - webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (citation), "-x-evo-main-cite"); - webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (citation), "type", "cite", NULL); - - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - citation, - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), - NULL); - - while ((sibling = webkit_dom_node_get_next_sibling (citation))) - webkit_dom_node_append_child (citation, sibling, NULL); - - remove_node (WEBKIT_DOM_NODE (cite_body)); - } -} - -/* For purpose of this function see e-mail-formatter-quote.c */ -static void -move_elements_to_body (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - WebKitDOMNodeList *list; - gint ii; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - list = webkit_dom_document_query_selector_all ( - document, "div[data-headers]", NULL); - for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (node), "data-headers"); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - node, - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (body)), - NULL); - - g_object_unref (node); - } - g_object_unref (list); - - list = webkit_dom_document_query_selector_all ( - document, "span.-x-evo-to-body[data-credits]", NULL); - for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) { - char *credits; - WebKitDOMElement *element; - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - element = e_html_editor_selection_get_paragraph_element (selection, document, -1, 0); - credits = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "data-credits"); - webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element), credits, NULL); - g_free (credits); - - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (element), - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (body)), - NULL); - - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); -} - -static void -repair_gmail_blockquotes (WebKitDOMDocument *document) -{ - WebKitDOMNodeList *list; - gint ii, length; - - list = webkit_dom_document_query_selector_all ( - document, "blockquote.gmail_quote", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "class"); - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "style"); - webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node), "type", "cite", NULL); - - if (!WEBKIT_DOM_IS_HTMLBR_ELEMENT (webkit_dom_node_get_last_child (node))) - webkit_dom_node_append_child ( - node, - WEBKIT_DOM_NODE ( - webkit_dom_document_create_element ( - document, "br", NULL)), - NULL); - g_object_unref (node); - } - g_object_unref (list); -} - -static void -remove_thunderbird_signature (WebKitDOMDocument *document) -{ - WebKitDOMElement *signature; - - signature = webkit_dom_document_query_selector ( - document, "pre.moz-signature", NULL); - if (signature) - remove_node (WEBKIT_DOM_NODE (signature)); -} - -/* Based on original use_pictograms() from GtkHTML */ -static const gchar *emoticons_chars = - /* 0 */ "DO)(|/PQ*!" - /* 10 */ "S\0:-\0:\0:-\0" - /* 20 */ ":\0:;=-\"\0:;" - /* 30 */ "B\"|\0:-'\0:X" - /* 40 */ "\0:\0:-\0:\0:-" - /* 50 */ "\0:\0:-\0:\0:-" - /* 60 */ "\0:\0:\0:-\0:\0" - /* 70 */ ":-\0:\0:-\0:\0"; -static gint emoticons_states[] = { - /* 0 */ 12, 17, 22, 34, 43, 48, 53, 58, 65, 70, - /* 10 */ 75, 0, -15, 15, 0, -15, 0, -17, 20, 0, - /* 20 */ -17, 0, -14, -20, -14, 28, 63, 0, -14, -20, - /* 30 */ -3, 63, -18, 0, -12, 38, 41, 0, -12, -2, - /* 40 */ 0, -4, 0, -10, 46, 0, -10, 0, -19, 51, - /* 50 */ 0, -19, 0, -11, 56, 0, -11, 0, -13, 61, - /* 60 */ 0, -13, 0, -6, 0, 68, -7, 0, -7, 0, - /* 70 */ -16, 73, 0, -16, 0, -21, 78, 0, -21, 0 }; -static const gchar *emoticons_icon_names[] = { - "face-angel", - "face-angry", - "face-cool", - "face-crying", - "face-devilish", - "face-embarrassed", - "face-kiss", - "face-laugh", /* not used */ - "face-monkey", /* not used */ - "face-plain", - "face-raspberry", - "face-sad", - "face-sick", - "face-smile", - "face-smile-big", - "face-smirk", - "face-surprise", - "face-tired", - "face-uncertain", - "face-wink", - "face-worried" -}; - -static gboolean -is_return_key (GdkEventKey *event) -{ - return ( - (event->keyval == GDK_KEY_Return) || - (event->keyval == GDK_KEY_Linefeed) || - (event->keyval == GDK_KEY_KP_Enter)); -} - -static void -html_editor_view_check_magic_links (EHTMLEditorView *view, - WebKitDOMRange *range, - gboolean include_space_by_user) -{ - gchar *node_text; - gchar **urls; - GRegex *regex = NULL; - GMatchInfo *match_info; - gint start_pos_url, end_pos_url; - WebKitDOMNode *node; - gboolean include_space = FALSE; - gboolean is_email_address = FALSE; - gboolean has_selection; - - if (!view->priv->magic_links) - return; - - if (include_space_by_user) - include_space = TRUE; - else - include_space = view->priv->space_key_pressed; - - node = webkit_dom_range_get_end_container (range, NULL); - has_selection = !webkit_dom_range_get_collapsed (range, NULL); - - if (view->priv->return_key_pressed) { - WebKitDOMNode* block; - - block = e_html_editor_get_parent_block_node_from_child (node); - /* Get previous block */ - if (!(block = webkit_dom_node_get_previous_sibling (block))) - return; - - /* If block is quoted content, get the last block there */ - while (block && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (block)) - block = webkit_dom_node_get_last_child (block); - - /* Get the last non-empty node */ - node = webkit_dom_node_get_last_child (block); - if (WEBKIT_DOM_IS_CHARACTER_DATA (node) && - webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)) == 0) - node = webkit_dom_node_get_previous_sibling (node); - } else { - e_html_editor_selection_save (view->priv->selection); - if (has_selection) { - WebKitDOMDocument *document; - WebKitDOMElement *selection_end_marker; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - node = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_end_marker)); - } - } - - if (!node || WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) - goto out; - - if (!WEBKIT_DOM_IS_TEXT (node)) { - if (webkit_dom_node_has_child_nodes (node)) - node = webkit_dom_node_get_first_child (node); - if (!WEBKIT_DOM_IS_TEXT (node)) - goto out; - } - - node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node)); - if (!(node_text && *node_text) || !g_utf8_validate (node_text, -1, NULL)) { - g_free (node_text); - goto out; - } - - if (strstr (node_text, "@") && !strstr (node_text, "://")) { - is_email_address = TRUE; - regex = g_regex_new (include_space ? E_MAIL_PATTERN_SPACE : E_MAIL_PATTERN, 0, 0, NULL); - } else - regex = g_regex_new (include_space ? URL_PATTERN_SPACE : URL_PATTERN, 0, 0, NULL); - - if (!regex) { - g_free (node_text); - goto out; - } - - g_regex_match_all (regex, node_text, G_REGEX_MATCH_NOTEMPTY, &match_info); - urls = g_match_info_fetch_all (match_info); - - if (urls) { - const gchar *end_of_match = NULL; - gchar *final_url, *url_end_raw, *url_text; - glong url_start, url_end, url_length; - WebKitDOMDocument *document; - WebKitDOMNode *url_text_node; - WebKitDOMElement *anchor; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - g_match_info_fetch_pos (match_info, 0, &start_pos_url, &end_pos_url); - - /* Get start and end position of url in node's text because positions - * that we get from g_match_info_fetch_pos are not UTF-8 aware */ - url_end_raw = g_strndup(node_text, end_pos_url); - url_end = g_utf8_strlen (url_end_raw, -1); - url_length = g_utf8_strlen (urls[0], -1); - - end_of_match = url_end_raw + end_pos_url - (include_space ? 3 : 2); - /* URLs are extremely unlikely to end with any punctuation, so - * strip any trailing punctuation off from link and put it after - * the link. Do the same for any closing double-quotes as well. */ - while (end_of_match && end_of_match != url_end_raw && strchr (URL_INVALID_TRAILING_CHARS, *end_of_match)) { - url_length--; - url_end--; - end_of_match--; - } - - url_start = url_end - url_length; - - webkit_dom_text_split_text ( - WEBKIT_DOM_TEXT (node), - include_space ? url_end - 1 : url_end, - NULL); - - webkit_dom_text_split_text ( - WEBKIT_DOM_TEXT (node), url_start, NULL); - url_text_node = webkit_dom_node_get_next_sibling (node); - url_text = webkit_dom_character_data_get_data ( - WEBKIT_DOM_CHARACTER_DATA (url_text_node)); - - if (g_str_has_prefix (url_text, "www.")) - final_url = g_strconcat ("http://" , url_text, NULL); - else if (is_email_address) - final_url = g_strconcat ("mailto:" , url_text, NULL); - else - final_url = g_strdup (url_text); - - /* Create and prepare new anchor element */ - anchor = webkit_dom_document_create_element (document, "A", NULL); - - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (anchor), - url_text, - NULL); - - webkit_dom_html_anchor_element_set_href ( - WEBKIT_DOM_HTML_ANCHOR_ELEMENT (anchor), - final_url); - - /* Insert new anchor element into document */ - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (anchor), - WEBKIT_DOM_NODE (url_text_node), - NULL); - - g_free (url_end_raw); - g_free (final_url); - g_free (url_text); - } else { - gboolean appending_to_link = FALSE; - gchar *href, *text, *url, *text_to_append = NULL; - gint diff; - WebKitDOMElement *parent; - WebKitDOMNode *prev_sibling; - - parent = webkit_dom_node_get_parent_element (node); - prev_sibling = webkit_dom_node_get_previous_sibling (node); - - /* If previous sibling is ANCHOR and actual text node is not beginning with - * space => we're appending to link */ - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) { - text_to_append = webkit_dom_node_get_text_content (node); - if (text_to_append && *text_to_append && - !strstr (text_to_append, " ") && - !(strchr (URL_INVALID_TRAILING_CHARS, *text_to_append) && - !(*text_to_append == '?' && strlen(text_to_append) > 1)) && - !g_str_has_prefix (text_to_append, UNICODE_NBSP)) { - - appending_to_link = TRUE; - parent = WEBKIT_DOM_ELEMENT (prev_sibling); - /* If the node(text) contains the some of unwanted characters - * split it into two nodes and select the right one. */ - if (g_str_has_suffix (text_to_append, UNICODE_NBSP) || - g_str_has_suffix (text_to_append, UNICODE_ZERO_WIDTH_SPACE)) { - webkit_dom_text_split_text ( - WEBKIT_DOM_TEXT (node), - g_utf8_strlen (text_to_append, -1) - 1, - NULL); - g_free (text_to_append); - text_to_append = webkit_dom_node_get_text_content (node); - } - } - } - - /* If parent is ANCHOR => we're editing the link */ - if ((!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) && !appending_to_link) || !text_to_append) { - g_match_info_free (match_info); - g_regex_unref (regex); - g_free (node_text); - g_free (text_to_append); - goto out; - } - - /* edit only if href and description are the same */ - href = webkit_dom_html_anchor_element_get_href ( - WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent)); - - if (appending_to_link) { - gchar *inner_text; - - inner_text = - webkit_dom_html_element_get_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (parent)), - - text = g_strconcat (inner_text, text_to_append, NULL); - g_free (inner_text); - } else - text = webkit_dom_html_element_get_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (parent)); - - element_remove_class (parent, "-x-evo-visited-link"); - - if (strstr (href, "://") && !strstr (text, "://")) { - url = strstr (href, "://") + 3; - diff = strlen (text) - strlen (url); - - if (text [strlen (text) - 1] != '/') - diff++; - - if ((g_strcmp0 (url, text) != 0 && ABS (diff) == 1) || appending_to_link) { - gchar *inner_html, *protocol, *new_href; - - protocol = g_strndup (href, strstr (href, "://") - href + 3); - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (parent)); - new_href = g_strconcat ( - protocol, inner_html, appending_to_link ? text_to_append : "", NULL); - - webkit_dom_html_anchor_element_set_href ( - WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent), - new_href); - - if (appending_to_link) { - webkit_dom_html_element_insert_adjacent_html ( - WEBKIT_DOM_HTML_ELEMENT (parent), - "beforeend", - text_to_append, - NULL); - - remove_node (node); - } - - g_free (new_href); - g_free (protocol); - g_free (inner_html); - } - } else { - diff = strlen (text) - strlen (href); - if (text [strlen (text) - 1] != '/') - diff++; - - if ((g_strcmp0 (href, text) != 0 && ABS (diff) == 1) || appending_to_link) { - gchar *inner_html; - gchar *new_href; - - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (parent)); - new_href = g_strconcat ( - inner_html, - appending_to_link ? text_to_append : "", - NULL); - - webkit_dom_html_anchor_element_set_href ( - WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent), - new_href); - - if (appending_to_link) { - webkit_dom_html_element_insert_adjacent_html ( - WEBKIT_DOM_HTML_ELEMENT (parent), - "beforeend", - text_to_append, - NULL); - - remove_node (node); - } - - g_free (new_href); - g_free (inner_html); - } - - } - g_free (text_to_append); - g_free (text); - g_free (href); - } - - g_match_info_free (match_info); - g_regex_unref (regex); - g_free (node_text); - out: - if (!view->priv->return_key_pressed) - e_html_editor_selection_restore (view->priv->selection); -} - -typedef struct _LoadContext LoadContext; - -struct _LoadContext { - EHTMLEditorView *view; - gchar *content_type; - gchar *name; - EEmoticon *emoticon; -}; - -static LoadContext * -emoticon_load_context_new (EHTMLEditorView *view, - EEmoticon *emoticon) -{ - LoadContext *load_context; - - load_context = g_slice_new0 (LoadContext); - load_context->view = view; - load_context->emoticon = emoticon; - - return load_context; -} - -static void -emoticon_load_context_free (LoadContext *load_context) -{ - g_free (load_context->content_type); - g_free (load_context->name); - g_slice_free (LoadContext, load_context); -} - -static void -insert_dash_history_event (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *event, *last; - GList *history; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - - event = g_new0 (EHTMLEditorViewHistoryEvent, 1); - event->type = HISTORY_INPUT; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - fragment = webkit_dom_document_create_document_fragment (document); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - webkit_dom_document_create_text_node (document, "-")), - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, TRUE)), - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, FALSE)), - NULL); - event->data.fragment = fragment; - - last = view->priv->history->data; - /* The dash event needs to have the same coordinates as the character - * that is right after it. */ - event->after.start.x = last->after.start.x; - event->after.start.y = last->after.start.y; - event->after.end.x = last->after.end.x; - event->after.end.y = last->after.end.y; - - history = view->priv->history->next; - if (history) { - EHTMLEditorViewHistoryEvent *item; - WebKitDOMNode *first_child; - - item = history->data; - - if (item->type != HISTORY_INPUT) - return; - - first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (item->data.fragment)); - if (WEBKIT_DOM_IS_TEXT (first_child)) { - guint diff; - - diff = event->after.start.x - item->after.start.x; - - /* We need to move the coordinate of the last - * event by one character. */ - last->after.start.x += diff; - last->after.end.x += diff; - - view->priv->history = g_list_insert_before ( - view->priv->history, history, event); - } - } -} - -static void -insert_delete_event (EHTMLEditorView *view, - WebKitDOMRange *range) -{ - EHTMLEditorViewHistoryEvent *ev; - WebKitDOMDocumentFragment *fragment; - - if (view->priv->undo_redo_in_progress) - return; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_DELETE; - - fragment = webkit_dom_range_clone_contents (range, NULL); - ev->data.fragment = fragment; - - e_html_editor_selection_get_selection_coordinates ( - view->priv->selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - ev->after.start.x = ev->before.start.x; - ev->after.start.y = ev->before.start.y; - ev->after.end.x = ev->before.start.x; - ev->after.end.y = ev->before.start.y; - - e_html_editor_view_insert_new_history_event (view, ev); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_AND; - - e_html_editor_view_insert_new_history_event (view, ev); -} - -static void -emoticon_insert_span (EHTMLEditorView *view, - EEmoticon *emoticon, - WebKitDOMElement *span) -{ - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev = NULL; - gboolean misplaced_selection = FALSE; - gchar *node_text = NULL; - const gchar *emoticon_start; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *node, *insert_before, *prev_sibling, *next_sibling; - WebKitDOMNode *selection_end_marker_parent, *inserted_node; - WebKitDOMRange *range; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - if (e_html_editor_selection_is_collapsed (selection)) { - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - if (!view->priv->smiley_written) { - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - if (view->priv->unicode_smileys) - ev->type = HISTORY_INPUT; - else { - ev->type = HISTORY_SMILEY; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - } - } - } - } else { - WebKitDOMRange *tmp_range; - - tmp_range = html_editor_view_get_dom_range (view); - insert_delete_event (view, tmp_range); - g_object_unref (tmp_range); - - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL); - - if (!view->priv->smiley_written) { - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - if (view->priv->unicode_smileys) - ev->type = HISTORY_INPUT; - else { - ev->type = HISTORY_SMILEY; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - } - } - } - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - } - - /* If the selection was not saved, move it into the first child of body */ - if (!selection_start_marker || !selection_end_marker) { - WebKitDOMHTMLElement *body; - WebKitDOMNode *child; - - body = webkit_dom_document_get_body (document); - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - add_selection_markers_into_element_start ( - document, - WEBKIT_DOM_ELEMENT (child), - &selection_start_marker, - &selection_end_marker); - - if (ev && !view->priv->unicode_smileys) - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - } - - /* Sometimes selection end marker is in body. Move it into next sibling */ - selection_end_marker_parent = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_end_marker)); - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (selection_end_marker_parent)) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)), - WEBKIT_DOM_NODE (selection_end_marker), - WEBKIT_DOM_NODE (selection_start_marker), - NULL); - if (ev && !view->priv->unicode_smileys) - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - } - selection_end_marker_parent = webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_end_marker)); - - /* Determine before what node we have to insert the smiley */ - insert_before = WEBKIT_DOM_NODE (selection_start_marker); - prev_sibling = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_start_marker)); - if (prev_sibling) { - if (webkit_dom_node_is_same_node ( - prev_sibling, WEBKIT_DOM_NODE (selection_end_marker))) { - insert_before = WEBKIT_DOM_NODE (selection_end_marker); - } else { - prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); - if (prev_sibling && - webkit_dom_node_is_same_node ( - prev_sibling, WEBKIT_DOM_NODE (selection_end_marker))) { - insert_before = WEBKIT_DOM_NODE (selection_end_marker); - } - } - } else - insert_before = WEBKIT_DOM_NODE (selection_start_marker); - - /* Look if selection is misplaced - that means that the selection was - * restored before the previously inserted smiley in situations when we - * are writing more smileys in a row */ - next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)); - if (next_sibling && WEBKIT_DOM_IS_ELEMENT (next_sibling)) - if (element_has_class (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-smiley-wrapper")) - misplaced_selection = TRUE; - - range = html_editor_view_get_dom_range (view); - node = webkit_dom_range_get_end_container (range, NULL); - g_object_unref (range); - if (WEBKIT_DOM_IS_TEXT (node)) - node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node)); - - if (misplaced_selection) { - /* Insert smiley and selection markers after it */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (insert_before), - WEBKIT_DOM_NODE (selection_start_marker), - webkit_dom_node_get_next_sibling (next_sibling), - NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (insert_before), - WEBKIT_DOM_NODE (selection_end_marker), - webkit_dom_node_get_next_sibling (next_sibling), - NULL); - if (view->priv->unicode_smileys) - inserted_node = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (insert_before), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (span)), - webkit_dom_node_get_next_sibling (next_sibling), - NULL); - else - inserted_node = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (insert_before), - WEBKIT_DOM_NODE (span), - webkit_dom_node_get_next_sibling (next_sibling), - NULL); - } else { - if (view->priv->unicode_smileys) - inserted_node = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (insert_before), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (span)), - insert_before, - NULL); - else - inserted_node = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (insert_before), - WEBKIT_DOM_NODE (span), - insert_before, - NULL); - } - - if (!view->priv->unicode_smileys) - webkit_dom_html_element_insert_adjacent_html ( - WEBKIT_DOM_HTML_ELEMENT (inserted_node), "afterend", "​", NULL); - - if (ev) { - WebKitDOMDocumentFragment *fragment; - WebKitDOMNode *node; - - fragment = webkit_dom_document_create_document_fragment (document); - node = webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (inserted_node), TRUE), - NULL); - if (view->priv->unicode_smileys) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, TRUE)), - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, FALSE)), - NULL); - } else - webkit_dom_html_element_insert_adjacent_html ( - WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "​", NULL); - ev->data.fragment = fragment; - } - - /* Remove the text that represents the text version of smiley that was - * written into the composer. */ - if (node_text && view->priv->smiley_written) { - emoticon_start = g_utf8_strrchr ( - node_text, -1, g_utf8_get_char (emoticon->text_face)); - /* Check if the written smiley is really the one that we inserted. */ - if (emoticon_start) { - /* The written smiley is the same as text version. */ - if (g_str_has_prefix (emoticon_start, emoticon->text_face)) { - webkit_dom_character_data_delete_data ( - WEBKIT_DOM_CHARACTER_DATA (node), - g_utf8_strlen (node_text, -1) - strlen (emoticon_start), - strlen (emoticon->text_face), - NULL); - } else if (strstr (emoticon->text_face, "-")) { - gboolean same = TRUE, compensate = FALSE; - gint ii = 0, jj = 0; - - /* Try to recognize smileys without the dash e.g. :). */ - while (emoticon_start[ii] && emoticon->text_face[jj]) { - if (emoticon_start[ii] == emoticon->text_face[jj]) { - if (emoticon->text_face[jj+1] && emoticon->text_face[jj+1] == '-') { - ii++; - jj+=2; - compensate = TRUE; - } else { - ii++; - jj++; - } - } else { - same = FALSE; - break; - } - } - - if (same) { - webkit_dom_character_data_delete_data ( - WEBKIT_DOM_CHARACTER_DATA (node), - g_utf8_strlen (node_text, -1) - strlen (emoticon_start), - ii, - NULL); - } - /* If we recognize smiley without dash, but we inserted - * the text version with dash we need it insert new - * history input event with that dash. */ - if (compensate) - insert_dash_history_event (view); - } - } - view->priv->smiley_written = FALSE; - } - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_selection_restore (selection); - - e_html_editor_view_set_changed (view, TRUE); - - g_free (node_text); -} - -static void -emoticon_read_async_cb (GFile *file, - GAsyncResult *result, - LoadContext *load_context) -{ - EHTMLEditorView *view = load_context->view; - EEmoticon *emoticon = load_context->emoticon; - GError *error = NULL; - gboolean html_mode; - gchar *mime_type; - gchar *base64_encoded, *output, *data; - GFileInputStream *input_stream; - GOutputStream *output_stream; - gssize size; - WebKitDOMElement *wrapper, *image, *smiley_text; - WebKitDOMDocument *document; - - input_stream = g_file_read_finish (file, result, &error); - g_return_if_fail (!error && input_stream); - - output_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); - - size = g_output_stream_splice ( - output_stream, G_INPUT_STREAM (input_stream), - G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error); - - if (error || (size == -1)) - goto out; - - mime_type = g_content_type_get_mime_type (load_context->content_type); - - data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream)); - base64_encoded = g_base64_encode ((const guchar *) data, size); - output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL); - - html_mode = e_html_editor_view_get_html_mode (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - /* Insert span with image representation and another one with text - * represetation and hide/show them dependant on active composer mode */ - wrapper = webkit_dom_document_create_element (document, "SPAN", NULL); - if (html_mode) - webkit_dom_element_set_attribute ( - wrapper, "class", "-x-evo-smiley-wrapper -x-evo-resizable-wrapper", NULL); - else - webkit_dom_element_set_attribute ( - wrapper, "class", "-x-evo-smiley-wrapper", NULL); - - image = webkit_dom_document_create_element (document, "IMG", NULL); - webkit_dom_element_set_attribute (image, "src", output, NULL); - webkit_dom_element_set_attribute (image, "data-inline", "", NULL); - webkit_dom_element_set_attribute (image, "data-name", load_context->name, NULL); - webkit_dom_element_set_attribute (image, "alt", emoticon->text_face, NULL); - webkit_dom_element_set_attribute (image, "class", "-x-evo-smiley-img", NULL); - if (!html_mode) - webkit_dom_element_set_attribute (image, "style", "display: none;", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (image), NULL); - - smiley_text = webkit_dom_document_create_element (document, "SPAN", NULL); - webkit_dom_element_set_attribute (smiley_text, "class", "-x-evo-smiley-text", NULL); - if (html_mode) - webkit_dom_element_set_attribute (smiley_text, "style", "display: none;", NULL); - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (smiley_text), emoticon->text_face, NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (smiley_text), NULL); - - emoticon_insert_span (view, emoticon, wrapper); - - g_free (base64_encoded); - g_free (output); - g_free (mime_type); - g_object_unref (output_stream); - out: - emoticon_load_context_free (load_context); -} - -static void -emoticon_query_info_async_cb (GFile *file, - GAsyncResult *result, - LoadContext *load_context) -{ - GError *error = NULL; - GFileInfo *info; - - info = g_file_query_info_finish (file, result, &error); - g_return_if_fail (!error && info); - - load_context->content_type = g_strdup (g_file_info_get_content_type (info)); - load_context->name = g_strdup (g_file_info_get_name (info)); - - g_file_read_async ( - file, G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback) emoticon_read_async_cb, load_context); - - g_object_unref (info); -} - -void -e_html_editor_view_insert_smiley (EHTMLEditorView *view, - EEmoticon *emoticon) -{ - GFile *file; - gchar *filename_uri; - LoadContext *load_context; - - if (e_html_editor_view_get_unicode_smileys (view)) { - WebKitDOMDocument *document; - WebKitDOMElement *wrapper; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - wrapper = webkit_dom_document_create_element (document, "SPAN", NULL); - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (wrapper), emoticon->unicode_character, NULL); - - emoticon_insert_span (view, emoticon, wrapper); - } else { - filename_uri = e_emoticon_get_uri (emoticon); - g_return_if_fail (filename_uri != NULL); - - load_context = emoticon_load_context_new (view, emoticon); - - file = g_file_new_for_uri (filename_uri); - g_file_query_info_async ( - file, "standard::*", G_FILE_QUERY_INFO_NONE, - G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback) emoticon_query_info_async_cb, load_context); - - g_free (filename_uri); - g_object_unref (file); - } -} - -static void -html_editor_view_check_magic_smileys (EHTMLEditorView *view, - WebKitDOMRange *range) -{ - gint pos; - gint state; - gint relative; - gint start; - gchar *node_text; - gunichar uc; - WebKitDOMNode *node; - - if (!view->priv->magic_smileys) - return; - - node = webkit_dom_range_get_end_container (range, NULL); - if (!WEBKIT_DOM_IS_TEXT (node)) - return; - - node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node)); - if (node_text == NULL) - return; - - start = webkit_dom_range_get_end_offset (range, NULL) - 1; - pos = start; - state = 0; - while (pos >= 0) { - uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos)); - relative = 0; - while (emoticons_chars[state + relative]) { - if (emoticons_chars[state + relative] == uc) - break; - relative++; - } - state = emoticons_states[state + relative]; - /* 0 .. not found, -n .. found n-th */ - if (state <= 0) - break; - pos--; - } - - /* Special case needed to recognize angel and devilish. */ - if (pos > 0 && state == -14) { - uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1)); - if (uc == 'O') { - state = -1; - pos--; - } else if (uc == '>') { - state = -5; - pos--; - } - } - - if (state < 0) { - const EEmoticon *emoticon; - - if (pos > 0) { - uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1)); - if (!g_unichar_isspace (uc)) { - g_free (node_text); - return; - } - } - - emoticon = (e_emoticon_chooser_lookup_emoticon ( - emoticons_icon_names[-state - 1])); - view->priv->smiley_written = TRUE; - e_html_editor_view_insert_smiley (view, (EEmoticon *) emoticon); - } - - g_free (node_text); -} - -static void -html_editor_view_set_links_active (EHTMLEditorView *view, - gboolean active) -{ - WebKitDOMDocument *document; - WebKitDOMElement *style; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - style = webkit_dom_document_get_element_by_id (document, "-x-evo-style-a"); - if (style) - remove_node (WEBKIT_DOM_NODE (style)); - - if (!active) { - WebKitDOMHTMLHeadElement *head; - head = webkit_dom_document_get_head (document); - - style = webkit_dom_document_create_element (document, "STYLE", NULL); - webkit_dom_element_set_id (style, "-x-evo-style-a"); - webkit_dom_element_set_attribute (style, "type", "text/css", NULL); - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (style), "a { cursor: text; }", NULL); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style), NULL); - } -} - -static void -fix_paragraph_structure_after_pressing_enter_after_smiley (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - WebKitDOMElement *element; - - element = webkit_dom_document_query_selector ( - document, "span.-x-evo-smiley-wrapper > br", NULL); - - if (element) { - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT ( - webkit_dom_node_get_parent_node (parent)), - UNICODE_ZERO_WIDTH_SPACE, - NULL); - } -} - -static gboolean -fix_paragraph_structure_after_pressing_enter (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - gboolean prev_is_heading = FALSE; - gint ii, length; - WebKitDOMNodeList *list; - - /* When pressing Enter on empty line in the list (or after heading elements) - * WebKit will end thatlist and inserts <div><br></div> so mark it for wrapping. */ - list = webkit_dom_document_query_selector_all ( - document, "body > div:not(.-x-evo-paragraph) > br", NULL); - - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *prev_sibling; - WebKitDOMNode *node = webkit_dom_node_get_parent_node ( - webkit_dom_node_list_item (list, ii)); - - prev_sibling = webkit_dom_node_get_previous_sibling (node); - if (prev_sibling && WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (prev_sibling)) - prev_is_heading = TRUE; - e_html_editor_selection_set_paragraph_style ( - selection, WEBKIT_DOM_ELEMENT (node), -1, 0, ""); - g_object_unref (node); - } - g_object_unref (list); - - return prev_is_heading; -} - -static gboolean -surround_text_with_paragraph_if_needed (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - WebKitDOMNode *node) -{ - WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (node); - WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (node); - WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); - WebKitDOMElement *element; - - /* All text in composer has to be written in div elements, so if - * we are writing something straight to the body, surround it with - * paragraph */ - if (WEBKIT_DOM_IS_TEXT (node) && - (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent) || - WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent))) { - element = e_html_editor_selection_put_node_into_paragraph ( - selection, document, node, TRUE); - if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent)) - webkit_dom_element_remove_attribute (element, "style"); - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) - remove_node (next_sibling); - - /* Tab character */ - if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "Apple-tab-span")) { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (element), - prev_sibling, - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (element)), - NULL); - } - - return TRUE; - } - - return FALSE; -} - -static void -body_keydown_event_cb (WebKitDOMElement *element, - WebKitDOMUIEvent *event, - EHTMLEditorView *view) -{ - glong key_code; - - key_code = webkit_dom_ui_event_get_key_code (event); - if (key_code == HTML_KEY_CODE_CONTROL) - html_editor_view_set_links_active (view, TRUE); - else if (key_code == HTML_KEY_CODE_DELETE || - key_code == HTML_KEY_CODE_BACKSPACE) - view->priv->dont_save_history_in_body_input = TRUE; -} - -static gboolean -save_history_before_event_in_table (EHTMLEditorView *view, - WebKitDOMRange *range) -{ - WebKitDOMNode *node; - WebKitDOMElement *block; - - node = webkit_dom_range_get_start_container (range, NULL); - if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) - block = WEBKIT_DOM_ELEMENT (node); - else - block = get_parent_block_element (node); - - if (block && WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (block)) { - EHTMLEditorViewHistoryEvent *ev; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_TABLE_INPUT; - - if (block) { - e_html_editor_selection_save (view->priv->selection); - ev->data.dom.from = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (block), TRUE); - e_html_editor_selection_restore (view->priv->selection); - } else - ev->data.dom.from = NULL; - - e_html_editor_selection_get_selection_coordinates ( - view->priv->selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - e_html_editor_view_insert_new_history_event (view, ev); - - return TRUE; - } - - return FALSE; -} - -static void -body_keypress_event_cb (WebKitDOMElement *element, - WebKitDOMUIEvent *event, - EHTMLEditorView *view) -{ - glong key_code; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - view->priv->return_key_pressed = FALSE; - view->priv->space_key_pressed = FALSE; - - key_code = webkit_dom_ui_event_get_key_code (event); - if (key_code == HTML_KEY_CODE_RETURN) - view->priv->return_key_pressed = TRUE; - else if (key_code == HTML_KEY_CODE_SPACE) - view->priv->space_key_pressed = TRUE; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - - if (save_history_before_event_in_table (view, range)) { - g_object_unref (range); - g_object_unref (dom_selection); - return; - } - - if (!webkit_dom_range_get_collapsed (range, NULL)) - insert_delete_event (view, range); - - if (view->priv->return_key_pressed) { - EHTMLEditorViewHistoryEvent *ev; - - /* Insert new history event for Return to have the right coordinates. - * The fragment will be added later. */ - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_INPUT; - - e_html_editor_selection_get_selection_coordinates ( - view->priv->selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - e_html_editor_view_insert_new_history_event (view, ev); - } - - g_object_unref (range); - g_object_unref (dom_selection); -} - -static gboolean -save_history_after_event_in_table (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *ev; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMElement *element; - WebKitDOMNode *node; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - if (!webkit_dom_dom_selection_get_range_count (dom_selection)) { - g_object_unref (dom_selection); - return FALSE; - } - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - - /* Find if writing into table. */ - node = webkit_dom_range_get_start_container (range, NULL); - if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) - element = WEBKIT_DOM_ELEMENT (node); - else - element = get_parent_block_element (node); - - g_object_unref (dom_selection); - g_object_unref (range); - - /* If writing to table we have to create different history event. */ - if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (element)) { - ev = view->priv->history->data; - if (ev->type != HISTORY_TABLE_INPUT) - return FALSE; - } else - return FALSE; - - e_html_editor_selection_save (view->priv->selection); - - e_html_editor_selection_get_selection_coordinates ( - view->priv->selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - - ev->data.dom.to = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), TRUE); - - e_html_editor_selection_restore (view->priv->selection); - - return TRUE; -} - -static void -save_history_for_input (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *ev; - glong offset; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range, *range_clone; - WebKitDOMNode *start_container; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (!webkit_dom_dom_selection_get_range_count (dom_selection)) { - g_object_unref (dom_selection); - return; - } - - if (view->priv->return_key_pressed) { - ev = view->priv->history->data; - if (ev->type != HISTORY_INPUT) { - g_object_unref (dom_selection); - return; - } - } else { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_INPUT; - } - - block_selection_changed_callbacks (view); - - e_html_editor_selection_get_selection_coordinates ( - view->priv->selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - range_clone = webkit_dom_range_clone_range (range, NULL); - offset = webkit_dom_range_get_start_offset (range_clone, NULL); - start_container = webkit_dom_range_get_start_container (range_clone, NULL); - if (offset > 0) - webkit_dom_range_set_start ( - range_clone, - start_container, - offset - 1, - NULL); - fragment = webkit_dom_range_clone_contents (range_clone, NULL); - /* We have to specially handle Return key press */ - if (view->priv->return_key_pressed) { - WebKitDOMElement *element_start, *element_end; - WebKitDOMNode *parent_start, *parent_end, *node; - - element_start = webkit_dom_document_create_element (document, "span", NULL); - webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element_start), NULL); - webkit_dom_dom_selection_modify (dom_selection, "move", "left", "character"); - g_object_unref (range); - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - element_end = webkit_dom_document_create_element (document, "span", NULL); - webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element_end), NULL); - - parent_start = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element_start)); - parent_end = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element_end)); - - while (parent_start && parent_end && !webkit_dom_node_is_same_node (parent_start, parent_end)) { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (parent_start, FALSE), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), - NULL); - parent_start = webkit_dom_node_get_parent_node (parent_start); - parent_end = webkit_dom_node_get_parent_node (parent_end); - } - - node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); - while (webkit_dom_node_get_next_sibling (node)) { - WebKitDOMNode *last_child; - - last_child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)); - webkit_dom_node_append_child ( - webkit_dom_node_get_previous_sibling (last_child), - last_child, - NULL); - } - - node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)); - while (webkit_dom_node_get_last_child (node)) { - node = webkit_dom_node_get_last_child (node); - } - - webkit_dom_node_append_child ( - node, - WEBKIT_DOM_NODE ( - webkit_dom_document_create_element (document, "br", NULL)), - NULL); - webkit_dom_node_append_child ( - node, - WEBKIT_DOM_NODE ( - create_selection_marker (document, TRUE)), - NULL); - webkit_dom_node_append_child ( - node, - WEBKIT_DOM_NODE ( - create_selection_marker (document, FALSE)), - NULL); - - remove_node (WEBKIT_DOM_NODE (element_start)); - remove_node (WEBKIT_DOM_NODE (element_end)); - - g_object_set_data ( - G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1)); - - webkit_dom_dom_selection_modify (dom_selection, "move", "right", "character"); - } else { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, TRUE)), - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, FALSE)), - NULL); - } - - g_object_unref (dom_selection); - g_object_unref (range); - g_object_unref (range_clone); - - unblock_selection_changed_callbacks (view); - - ev->data.fragment = fragment; - if (!view->priv->return_key_pressed) - e_html_editor_view_insert_new_history_event (view, ev); -} - -static gboolean -force_spell_check_on_timeout (EHTMLEditorView *view) -{ - e_html_editor_view_force_spell_check_in_viewport (view); - view->priv->spell_check_on_scroll_event_source_id = 0; - return FALSE; -} - -static void -body_scroll_event_cb (WebKitDOMElement *element, - WebKitDOMEvent *event, - EHTMLEditorView *view) -{ - if (!view->priv->inline_spelling) - return; - - if (view->priv->spell_check_on_scroll_event_source_id > 0) - g_source_remove (view->priv->spell_check_on_scroll_event_source_id); - - view->priv->spell_check_on_scroll_event_source_id = - g_timeout_add_seconds (1, (GSourceFunc)force_spell_check_on_timeout, view); -} - -static void -body_input_event_cb (WebKitDOMElement *element, - WebKitDOMEvent *event, - EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - gboolean do_spell_check = FALSE; - WebKitDOMNode *node; - WebKitDOMRange *range = html_editor_view_get_dom_range (view); - WebKitDOMDocument *document; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - e_html_editor_view_set_changed (view, TRUE); - - if (view->priv->undo_redo_in_progress) { - view->priv->undo_redo_in_progress = FALSE; - view->priv->dont_save_history_in_body_input = FALSE; - do_spell_check = TRUE; - goto out; - } - - /* When the Backspace is pressed in a bulleted list item with just one - * character left in it, WebKit will create another BR element in the - * item. */ - if (!view->priv->html_mode) { - WebKitDOMElement *element; - - element = webkit_dom_document_query_selector ( - document, "ul[data-evo-plain-text] > li > br + br", NULL); - - if (element) - remove_node (WEBKIT_DOM_NODE (element)); - } - - if (!save_history_after_event_in_table (view)) { - if (!view->priv->dont_save_history_in_body_input) - save_history_for_input (view); - else - do_spell_check = TRUE; - } - - /* Don't try to look for smileys if we are deleting text. */ - if (!view->priv->dont_save_history_in_body_input) - html_editor_view_check_magic_smileys (view, range); - - view->priv->dont_save_history_in_body_input = FALSE; - - if (view->priv->return_key_pressed || view->priv->space_key_pressed) { - html_editor_view_check_magic_links (view, range, FALSE); - if (view->priv->return_key_pressed) { - if (fix_paragraph_structure_after_pressing_enter (selection, document) && - view->priv->html_mode) { - /* When the return is pressed in a H1-6 element, WebKit doesn't - * continue with the same element, but creates normal paragraph, - * so we have to unset the bold font. */ - view->priv->undo_redo_in_progress = TRUE; - e_html_editor_selection_set_bold (selection, FALSE); - view->priv->undo_redo_in_progress = FALSE; - } - - fix_paragraph_structure_after_pressing_enter_after_smiley ( - selection, document); - - do_spell_check = TRUE; - } - } else { - WebKitDOMNode *node; - - node = webkit_dom_range_get_end_container (range, NULL); - - if (surround_text_with_paragraph_if_needed (selection, document, node)) { - WebKitDOMElement *element; - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - e_html_editor_selection_restore (selection); - } - - if (WEBKIT_DOM_IS_TEXT (node)) { - gchar *text; - - text = webkit_dom_node_get_text_content (node); - - if (text && *text && *text != ' ' && !g_str_has_prefix (text, UNICODE_NBSP)) { - gboolean valid = FALSE; - - if (*text == '?' && strlen (text) > 1) - valid = TRUE; - else if (!strchr (URL_INVALID_TRAILING_CHARS, *text)) - valid = TRUE; - - if (valid) { - WebKitDOMNode *prev_sibling; - - prev_sibling = webkit_dom_node_get_previous_sibling (node); - - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) - html_editor_view_check_magic_links (view, range, FALSE); - } - } - g_free (text); - } - } - - node = webkit_dom_range_get_end_container (range, NULL); - - /* After toggling monospaced format, we are using UNICODE_ZERO_WIDTH_SPACE - * to move caret into right space. When this callback is called it is not - * necessary anymore so remove it */ - if (view->priv->html_mode) { - WebKitDOMElement *parent = webkit_dom_node_get_parent_element (node); - - if (parent) { - WebKitDOMNode *prev_sibling; - - prev_sibling = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (parent)); - - if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) { - gchar *text = webkit_dom_node_get_text_content ( - prev_sibling); - - if (g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0) - remove_node (prev_sibling); - - g_free (text); - } - - } - } - - /* If text before caret includes UNICODE_ZERO_WIDTH_SPACE character, remove it */ - if (WEBKIT_DOM_IS_TEXT (node)) { - gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node)); - glong length = webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)); - WebKitDOMNode *parent; - - /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE - * character as when we will remove it it will collapse */ - if (length > 1) { - if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE)) - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL); - else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE)) - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (node), length - 1, 1, "", NULL); - } - g_free (text); - - parent = webkit_dom_node_get_parent_node (node); - if ((WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent) || - WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent)) && - !element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-paragraph")) { - if (e_html_editor_view_get_html_mode (view)) { - element_add_class ( - WEBKIT_DOM_ELEMENT (parent), "-x-evo-paragraph"); - } else { - e_html_editor_selection_set_paragraph_style ( - selection, - WEBKIT_DOM_ELEMENT (parent), - -1, 0, ""); - } - } - - /* When new smiley is added we have to use UNICODE_HIDDEN_SPACE to set the - * caret position to right place. It is removed when user starts typing. But - * when the user will press left arrow he will move the caret into - * smiley wrapper. If he will start to write there we have to move the written - * text out of the wrapper and move caret to right place */ - if (WEBKIT_DOM_IS_ELEMENT (parent) && - element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text")) { - gchar *text; - WebKitDOMCharacterData *data; - WebKitDOMText *text_node; - - /* Split out the newly written character to its own text node, */ - data = WEBKIT_DOM_CHARACTER_DATA (node); - parent = webkit_dom_node_get_parent_node (parent); - text = webkit_dom_character_data_substring_data ( - data, - webkit_dom_character_data_get_length (data) - 1, - 1, - NULL); - webkit_dom_character_data_delete_data ( - data, - webkit_dom_character_data_get_length (data) - 1, - 1, - NULL); - text_node = webkit_dom_document_create_text_node (document, text); - g_free (text); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - WEBKIT_DOM_NODE ( - create_selection_marker (document, FALSE)), - webkit_dom_node_get_next_sibling (parent), - NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - WEBKIT_DOM_NODE ( - create_selection_marker (document, TRUE)), - webkit_dom_node_get_next_sibling (parent), - NULL); - /* Move the text node outside of smiley. */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - WEBKIT_DOM_NODE (text_node), - webkit_dom_node_get_next_sibling (parent), - NULL); - e_html_editor_selection_restore (selection); - } - } - - /* Writing into quoted content */ - if (!view->priv->html_mode) { - gint citation_level; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *node, *parent; - - node = webkit_dom_range_get_end_container (range, NULL); - - citation_level = get_citation_level (node, FALSE); - if (citation_level == 0) - goto out; - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - if (selection_start_marker) - goto out; - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - /* If the selection was not saved, move it into the first child of body */ - if (!selection_start_marker || !selection_end_marker) { - WebKitDOMHTMLElement *body; - WebKitDOMNode *child; - - body = webkit_dom_document_get_body (document); - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - add_selection_markers_into_element_start ( - document, - WEBKIT_DOM_ELEMENT (child), - &selection_start_marker, - &selection_end_marker); - } - - /* We have to process elements only inside normal block */ - parent = WEBKIT_DOM_NODE (get_parent_block_element ( - WEBKIT_DOM_NODE (selection_start_marker))); - if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent)) { - e_html_editor_selection_restore (selection); - goto out; - } - - if (selection_start_marker) { - gchar *content; - gint text_length, word_wrap_length, length; - WebKitDOMElement *block; - gboolean remove_quoting = FALSE; - - word_wrap_length = - e_html_editor_selection_get_word_wrap_length (selection); - length = word_wrap_length - 2 * citation_level; - - block = WEBKIT_DOM_ELEMENT (parent); - if (webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block), ".-x-evo-quoted", NULL)) { - WebKitDOMNode *prev_sibling; - - prev_sibling = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_end_marker)); - - if (WEBKIT_DOM_IS_ELEMENT (prev_sibling)) - remove_quoting = element_has_class ( - WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted"); - } - - content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (block)); - text_length = g_utf8_strlen (content, -1); - g_free (content); - - /* Wrap and quote the line */ - if (!remove_quoting && text_length >= word_wrap_length) { - remove_quoting_from_element (block); - - block = e_html_editor_selection_wrap_paragraph_length ( - selection, block, length); - webkit_dom_node_normalize (WEBKIT_DOM_NODE (block)); - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, WEBKIT_DOM_ELEMENT (block), citation_level); - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - if (!selection_start_marker) - add_selection_markers_into_element_end ( - document, - WEBKIT_DOM_ELEMENT (block), - NULL, - NULL); - - e_html_editor_selection_restore (selection); - do_spell_check = TRUE; - - goto out; - } - } - e_html_editor_selection_restore (selection); - } - out: - if (do_spell_check) - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - g_object_unref (range); -} - -static void -remove_empty_blocks (WebKitDOMDocument *document) -{ - gint ii, length; - WebKitDOMNodeList *list; - - list = webkit_dom_document_query_selector_all ( - document, "blockquote[type=cite] > :empty", NULL); - - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - list = webkit_dom_document_query_selector_all ( - document, "blockquote[type=cite]:empty", NULL); - - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - remove_node (node); - g_object_unref (node); - } - - g_object_unref (list); -} - -/* Following two functions are used when deleting the selection inside - * the quoted content. The thing is that normally the quote marks are not - * selectable by user. But this caused a lof of problems for WebKit when removing - * the selection. This will avoid it as when the delete or backspace key is pressed - * we will make the quote marks user selectable so they will act as any other text. - * On HTML keyup event callback we will make them again non-selectable. */ -static void -disable_quote_marks_select (WebKitDOMDocument *document) -{ - WebKitDOMHTMLHeadElement *head; - WebKitDOMElement *style_element; - - head = webkit_dom_document_get_head (document); - - if (!webkit_dom_document_get_element_by_id (document, "-x-evo-quote-style")) { - style_element = webkit_dom_document_create_element (document, "style", NULL); - webkit_dom_element_set_id (style_element, "-x-evo-quote-style"); - webkit_dom_element_set_attribute (style_element, "type", "text/css", NULL); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (style_element), - ".-x-evo-quoted { -webkit-user-select: none; }", - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style_element), NULL); - } -} - -static void -enable_quote_marks_select (WebKitDOMDocument *document) -{ - WebKitDOMElement *style_element; - - if ((style_element = webkit_dom_document_get_element_by_id (document, "-x-evo-quote-style"))) - remove_node (WEBKIT_DOM_NODE (style_element)); -} - -static void -remove_node_and_parents_if_empty (WebKitDOMNode *node) -{ - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (node)); - - remove_node (WEBKIT_DOM_NODE (node)); - - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - WebKitDOMNode *tmp; - - tmp = webkit_dom_node_get_parent_node (parent); - remove_node_if_empty (parent); - parent = tmp; - } -} - -static void -merge_siblings_if_necessary (WebKitDOMDocument *document, - WebKitDOMDocumentFragment *deleted_content) -{ - gboolean equal_nodes; - gint ii, length; - WebKitDOMElement *element, *prev_element; - WebKitDOMNode *child; - WebKitDOMNodeList *list; - - if ((element = webkit_dom_document_get_element_by_id (document, "-x-evo-main-cite"))) - webkit_dom_element_remove_attribute (element, "id"); - - element = webkit_dom_document_query_selector (document, "blockquote:not([data-evo-query-skip]) + blockquote", NULL); - if (!element) - goto signature; - repeat: - child = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - if (WEBKIT_DOM_IS_ELEMENT (child)) - prev_element = WEBKIT_DOM_ELEMENT (child); - else - goto signature; - - equal_nodes = webkit_dom_node_is_equal_node ( - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), FALSE), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (prev_element), FALSE)); - - if (equal_nodes) { - if (webkit_dom_element_get_child_element_count (element) > - webkit_dom_element_get_child_element_count (prev_element)) { - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (prev_element), child, NULL); - remove_node (WEBKIT_DOM_NODE (element)); - } else { - while ((child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (prev_element)))) - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (element), - child, - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (element)), - NULL); - remove_node (WEBKIT_DOM_NODE (prev_element)); - } - } else - webkit_dom_element_set_attribute (element, "data-evo-query-skip", "", NULL); - - element = webkit_dom_document_query_selector (document, "blockquote:not([data-evo-query-skip]) + blockquote", NULL); - if (element) - goto repeat; - - signature: - list = webkit_dom_document_query_selector_all ( - document, "blockquote[data-evo-query-skip]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (node), "data-evo-query-skip"); - g_object_unref (node); - } - g_object_unref (list); - - if (!deleted_content) - return; - - /* Replace the corrupted signatures with the right one. */ - element = webkit_dom_document_query_selector ( - document, ".-x-evo-signature-wrapper + .-x-evo-signature-wrapper", NULL); - if (element) { - WebKitDOMElement *right_signature; - - right_signature = webkit_dom_document_fragment_query_selector ( - deleted_content, ".-x-evo-signature-wrapper", NULL); - remove_node (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element))); - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (right_signature), TRUE), - WEBKIT_DOM_NODE (element), - NULL); - } -} - -/* This will fix the structure after the situations where some text - * inside the quoted content is selected and afterwards deleted with - * BackSpace or Delete. */ -static void -body_key_up_event_process_backspace_or_delete (EHTMLEditorView *view, - gboolean delete) -{ - EHTMLEditorSelection *selection; - gint level; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMDocument *document; - WebKitDOMNode *parent, *node; - - if (view->priv->html_mode) - return; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - disable_quote_marks_select (document); - /* Remove empty blocks if presented. */ - remove_empty_blocks (document); - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_save (selection); - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - /* If we deleted a selection the caret will be inside the quote marks, fix it. */ - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); - if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quote-character")) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - webkit_dom_node_get_parent_node (parent)), - WEBKIT_DOM_NODE (selection_end_marker), - webkit_dom_node_get_next_sibling ( - webkit_dom_node_get_parent_node (parent)), - NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - webkit_dom_node_get_parent_node (parent)), - WEBKIT_DOM_NODE (selection_start_marker), - webkit_dom_node_get_next_sibling ( - webkit_dom_node_get_parent_node (parent)), - NULL); - } - - /* Under some circumstances we will end with block inside the citation - * that has the quote marks removed and we have to reinsert them back. */ - level = get_citation_level (WEBKIT_DOM_NODE (selection_start_marker), FALSE); - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)); - if (level > 0 && node && !WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) { - WebKitDOMElement *block; - - block = WEBKIT_DOM_ELEMENT (e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker))); - - remove_quoting_from_element (block); - if (element_has_class (block, "-x-evo-paragraph")) { - gint length, word_wrap_length; - - word_wrap_length = e_html_editor_selection_get_word_wrap_length (selection); - length = word_wrap_length - 2 * level; - block = e_html_editor_selection_wrap_paragraph_length ( - selection, block, length); - webkit_dom_node_normalize (WEBKIT_DOM_NODE (block)); - } - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, block, level); - } else if (level > 0 && !node) { - WebKitDOMNode *prev_sibling; - - prev_sibling = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_start_marker)); - if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted") && - !webkit_dom_node_get_previous_sibling (prev_sibling)) - webkit_dom_node_append_child ( - parent, - WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), - NULL); - } - - merge_siblings_if_necessary (document, NULL); - - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_for_current_paragraph (view); -} - -static void -body_key_up_event_process_return_key (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *parent; - - /* If the return is pressed in an unordered list in plain text mode - * the caret is moved to the "*" character before the newly inserted - * item. It looks like it is not enough that the item has BR element - * inside, but we have to again use the zero width space character - * to fix the situation. */ - if (view->priv->html_mode) - return; - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_save (selection); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); - if (!WEBKIT_DOM_IS_HTMLLI_ELEMENT (parent) || - !WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (webkit_dom_node_get_parent_node (parent))) { - e_html_editor_selection_restore (selection); - return; - } - - if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)) && - (!webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)) || - WEBKIT_DOM_IS_HTMLBR_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker))))) - webkit_dom_html_element_insert_adjacent_text ( - WEBKIT_DOM_HTML_ELEMENT (parent), - "afterbegin", - UNICODE_ZERO_WIDTH_SPACE, - NULL); - - e_html_editor_selection_restore (selection); -} - -static void -set_monospace_font_family_on_body (WebKitDOMElement *body, - gboolean html_mode) -{ - /* If copying some content in view, WebKit adds various information about - * the content's style (such as color, font size, ..) to the resulting HTML - * to correctly apply the style when pasting the content later. The thing - * is that in plain text mode the only font allowed is the monospaced one, - * but we are forcing it through user style sheet in WebKitWebSettings and - * sadly WebKit doesn't count with it, so when the content is pasted, - * WebKit wraps it inside SPANs and sets the font-family style on them. - * The problem is that when we switch to the HTML mode, the pasted content - * will have the monospaced font set. To avoid it we need to set the - * font-family style to the body, so WebKit will know about it and will - * avoid the described behaviour. */ - /* When we are deleting a content from the PRE elements we need to turn - * this off, otherwise we will end with the same unwanted behavior (the - * text between the caret and the end of the element will be wrapped - * inside a SPAN element. */ - if (!html_mode) { - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (body), "data-style", "style"); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), - "style", - "font-family: Monospace;", - NULL); - } else { - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (body), "style", "data-style"); - } -} - -static void -body_keyup_event_cb (WebKitDOMElement *element, - WebKitDOMUIEvent *event, - EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - glong key_code; - - if (!view->priv->composition_in_progress) - e_html_editor_view_register_input_event_listener_on_body (view); - - selection = e_html_editor_view_get_selection (view); - if (!e_html_editor_selection_is_collapsed (selection)) - return; - - key_code = webkit_dom_ui_event_get_key_code (event); - if (key_code == HTML_KEY_CODE_BACKSPACE || key_code == HTML_KEY_CODE_DELETE) { - if (!view->priv->html_mode) { - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - - document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); - body = webkit_dom_document_get_body (document); - - set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), FALSE); - } - body_key_up_event_process_backspace_or_delete (view, key_code == HTML_KEY_CODE_DELETE); - - /* The content was wrapped and the coordinates - * of caret could be changed, so renew them. But - * only do that when we are not redoing a history - * event, otherwise it would modify the history. */ - if (view->priv->renew_history_after_coordinates) { - EHTMLEditorViewHistoryEvent *ev = NULL; - - ev = view->priv->history->data; - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - } - } else if (key_code == HTML_KEY_CODE_CONTROL) - html_editor_view_set_links_active (view, FALSE); - else if (key_code == HTML_KEY_CODE_RETURN) - body_key_up_event_process_return_key (view); -} - -static void -clipboard_text_received_for_paste_as_text (GtkClipboard *clipboard, - const gchar *text, - EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev = NULL; - - if (!text || !*text) - return; - - selection = e_html_editor_view_get_selection (view); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - gboolean collapsed; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_PASTE_AS_TEXT; - - collapsed = e_html_editor_selection_is_collapsed (selection); - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - if (!collapsed) { - ev->before.end.x = ev->before.start.x; - ev->before.end.y = ev->before.start.y; - } - ev->data.string.from = NULL; - ev->data.string.to = g_strdup (text); - } - - e_html_editor_selection_insert_as_text (selection, text); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } -} - -static void -clipboard_text_received (GtkClipboard *clipboard, - const gchar *text, - EHTMLEditorView *view) -{ - e_html_editor_view_insert_quoted_text (view, text); -} - -static void -html_editor_view_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_CHANGED: - e_html_editor_view_set_changed ( - E_HTML_EDITOR_VIEW (object), - g_value_get_boolean (value)); - return; - - case PROP_HTML_MODE: - e_html_editor_view_set_html_mode ( - E_HTML_EDITOR_VIEW (object), - g_value_get_boolean (value)); - return; - - case PROP_INLINE_SPELLING: - e_html_editor_view_set_inline_spelling ( - E_HTML_EDITOR_VIEW (object), - g_value_get_boolean (value)); - return; - - case PROP_MAGIC_LINKS: - e_html_editor_view_set_magic_links ( - E_HTML_EDITOR_VIEW (object), - g_value_get_boolean (value)); - return; - - case PROP_MAGIC_SMILEYS: - e_html_editor_view_set_magic_smileys ( - E_HTML_EDITOR_VIEW (object), - g_value_get_boolean (value)); - return; - - case PROP_UNICODE_SMILEYS: - e_html_editor_view_set_unicode_smileys ( - E_HTML_EDITOR_VIEW (object), - g_value_get_boolean (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -html_editor_view_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_CAN_COPY: - g_value_set_boolean ( - value, webkit_web_view_can_copy_clipboard ( - WEBKIT_WEB_VIEW (object))); - return; - - case PROP_CAN_CUT: - g_value_set_boolean ( - value, webkit_web_view_can_cut_clipboard ( - WEBKIT_WEB_VIEW (object))); - return; - - case PROP_CAN_PASTE: - g_value_set_boolean ( - value, webkit_web_view_can_paste_clipboard ( - WEBKIT_WEB_VIEW (object))); - return; - - case PROP_CAN_REDO: - g_value_set_boolean ( - value, e_html_editor_view_can_redo ( - E_HTML_EDITOR_VIEW (object))); - return; - - case PROP_CAN_UNDO: - g_value_set_boolean ( - value, e_html_editor_view_can_undo ( - E_HTML_EDITOR_VIEW (object))); - return; - - case PROP_CHANGED: - g_value_set_boolean ( - value, e_html_editor_view_get_changed ( - E_HTML_EDITOR_VIEW (object))); - return; - - case PROP_HTML_MODE: - g_value_set_boolean ( - value, e_html_editor_view_get_html_mode ( - E_HTML_EDITOR_VIEW (object))); - return; - - case PROP_INLINE_SPELLING: - g_value_set_boolean ( - value, e_html_editor_view_get_inline_spelling ( - E_HTML_EDITOR_VIEW (object))); - return; - - case PROP_MAGIC_LINKS: - g_value_set_boolean ( - value, e_html_editor_view_get_magic_links ( - E_HTML_EDITOR_VIEW (object))); - return; - - case PROP_MAGIC_SMILEYS: - g_value_set_boolean ( - value, e_html_editor_view_get_magic_smileys ( - E_HTML_EDITOR_VIEW (object))); - return; - - case PROP_UNICODE_SMILEYS: - g_value_set_boolean ( - value, e_html_editor_view_get_unicode_smileys ( - E_HTML_EDITOR_VIEW (object))); - return; - - case PROP_SPELL_CHECKER: - g_value_set_object ( - value, e_html_editor_view_get_spell_checker ( - E_HTML_EDITOR_VIEW (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -free_history_event_content (EHTMLEditorViewHistoryEvent *event) -{ - switch (event->type) { - case HISTORY_INPUT: - case HISTORY_DELETE: - case HISTORY_CITATION_SPLIT: - case HISTORY_IMAGE: - case HISTORY_SMILEY: - case HISTORY_REMOVE_LINK: - case HISTORY_BLOCKQUOTE: - if (event->data.fragment != NULL) - g_clear_object (&event->data.fragment); - break; - case HISTORY_FONT_COLOR: - case HISTORY_PASTE: - case HISTORY_PASTE_AS_TEXT: - case HISTORY_PASTE_QUOTED: - case HISTORY_INSERT_HTML: - case HISTORY_REPLACE: - case HISTORY_REPLACE_ALL: - if (event->data.string.from != NULL) - g_free (event->data.string.from); - if (event->data.string.to != NULL) - g_free (event->data.string.to); - break; - case HISTORY_HRULE_DIALOG: - case HISTORY_IMAGE_DIALOG: - case HISTORY_CELL_DIALOG: - case HISTORY_TABLE_DIALOG: - case HISTORY_TABLE_INPUT: - case HISTORY_PAGE_DIALOG: - case HISTORY_UNQUOTE: - case HISTORY_LINK_DIALOG: - if (event->data.dom.from != NULL) - g_clear_object (&event->data.dom.from); - if (event->data.dom.to != NULL) - g_clear_object (&event->data.dom.to); - break; - default: - break; - } -} - -static void -free_history_event (EHTMLEditorViewHistoryEvent *event) -{ - if (event == NULL) - return; - - free_history_event_content (event); - - g_free (event); -} - -static void -html_editor_view_dispose (GObject *object) -{ - EHTMLEditorViewPrivate *priv; - - priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (object); - - g_clear_object (&priv->selection); - - if (priv->spell_check_on_scroll_event_source_id > 0) { - g_source_remove (priv->spell_check_on_scroll_event_source_id); - priv->spell_check_on_scroll_event_source_id = 0; - } - - if (priv->aliasing_settings != NULL) { - g_signal_handlers_disconnect_by_data (priv->aliasing_settings, object); - g_object_unref (priv->aliasing_settings); - priv->aliasing_settings = NULL; - } - - if (priv->font_settings != NULL) { - g_signal_handlers_disconnect_by_data (priv->font_settings, object); - g_object_unref (priv->font_settings); - priv->font_settings = NULL; - } - - if (priv->mail_settings != NULL) { - g_signal_handlers_disconnect_by_data (priv->mail_settings, object); - g_object_unref (priv->mail_settings); - priv->mail_settings = NULL; - } - - if (priv->history != NULL) { - g_list_free_full (priv->history, (GDestroyNotify) free_history_event); - priv->history = NULL; - } - - if (priv->owner_change_clipboard_cb_id > 0) { - g_signal_handler_disconnect ( - gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), - priv->owner_change_clipboard_cb_id); - priv->owner_change_clipboard_cb_id = 0; - } - - if (priv->owner_change_primary_cb_id > 0) { - g_signal_handler_disconnect ( - gtk_clipboard_get (GDK_SELECTION_PRIMARY), - priv->owner_change_primary_cb_id); - priv->owner_change_primary_cb_id = 0; - } - - g_hash_table_remove_all (priv->inline_images); - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_html_editor_view_parent_class)->dispose (object); -} - -static void -html_editor_view_finalize (GObject *object) -{ - EHTMLEditorViewPrivate *priv; - - priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (object); - - g_hash_table_destroy (priv->inline_images); - - if (priv->old_settings) { - g_hash_table_destroy (priv->old_settings); - priv->old_settings = NULL; - } - - if (priv->post_reload_operations) { - g_warn_if_fail (g_queue_is_empty (priv->post_reload_operations)); - - g_queue_free (priv->post_reload_operations); - priv->post_reload_operations = NULL; - } - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_html_editor_view_parent_class)->finalize (object); -} - -static void -html_editor_view_constructed (GObject *object) -{ - e_extensible_load_extensions (E_EXTENSIBLE (object)); - - /* Chain up to parent's constructed() method. */ - G_OBJECT_CLASS (e_html_editor_view_parent_class)->constructed (object); -} - -static void -html_editor_view_move_selection_on_point (GtkWidget *widget) -{ - gint x, y; - GdkDeviceManager *device_manager; - GdkDevice *pointer; - - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (widget)); - - device_manager = gdk_display_get_device_manager ( - gtk_widget_get_display (GTK_WIDGET (widget))); - pointer = gdk_device_manager_get_client_pointer (device_manager); - gdk_window_get_device_position ( - gtk_widget_get_window (GTK_WIDGET (widget)), pointer, &x, &y, NULL); - - e_html_editor_selection_set_on_point ( - e_html_editor_view_get_selection (E_HTML_EDITOR_VIEW (widget)), x, y); -} - -static gboolean -html_editor_view_button_press_event (GtkWidget *widget, - GdkEventButton *event) -{ - EHTMLEditorView *view; - EHTMLEditorSelection *selection; - gboolean event_handled, collapsed; - - view = E_HTML_EDITOR_VIEW (widget); - selection = e_html_editor_view_get_selection (view); - collapsed = e_html_editor_selection_is_collapsed (selection); - - if (event->button == 2) { - /* Middle click paste */ - html_editor_view_move_selection_on_point (widget); - /* Remember, that we are pasting primary clipboard to return - * correct value in e_html_editor_view_is_pasting_content_from_itself. */ - view->priv->pasting_primary_clipboard = TRUE; - g_signal_emit (widget, signals[PASTE_PRIMARY_CLIPBOARD], 0); - event_handled = TRUE; - } else if (event->button == 3) { - if (collapsed) - html_editor_view_move_selection_on_point (widget); - g_signal_emit ( - widget, signals[POPUP_EVENT], - 0, event, &event_handled); - } else { - event_handled = FALSE; - } - - if (event_handled) - return TRUE; - - /* Chain up to parent's button_press_event() method. */ - return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)-> - button_press_event (widget, event); -} - -static gboolean -html_editor_view_button_release_event (GtkWidget *widget, - GdkEventButton *event) -{ - WebKitWebView *webview; - WebKitHitTestResult *hit_test; - WebKitHitTestResultContext context; - WebKitDOMNode *node; - gchar *uri; - - webview = WEBKIT_WEB_VIEW (widget); - hit_test = webkit_web_view_get_hit_test_result (webview, event); - - g_object_get ( - hit_test, - "context", &context, - "link-uri", &uri, - "inner-node", &node, - NULL); - - g_object_unref (hit_test); - - /* Left click on a link */ - if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) && - (event->button == 1)) { - - /* Ctrl + Left Click on link opens it, otherwise ignore the - * click completely */ - if (event->state & GDK_CONTROL_MASK) { - GtkWidget *toplevel; - GdkScreen *screen; - WebKitDOMElement *element; - - toplevel = gtk_widget_get_toplevel (widget); - screen = gtk_window_get_screen (GTK_WINDOW (toplevel)); - gtk_show_uri (screen, uri, event->time, NULL); - g_free (uri); - - element = e_html_editor_dom_node_find_parent_element (node, "A"); - if (element) - element_add_class (element, "-x-evo-visited-link"); - } - - return TRUE; - } - - g_free (uri); - - /* Chain up to parent's button_release_event() method. */ - return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)-> - button_release_event (widget, event); -} - -static gboolean -prevent_from_deleting_last_element_in_body (EHTMLEditorView *view) -{ - gboolean ret_val = FALSE; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - WebKitDOMNode *node; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - if (!node || !webkit_dom_node_get_next_sibling (node)) { - gchar *content; - - content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (body)); - - if (!content || !*content) - ret_val = TRUE; - - g_free (content); - - if (webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (body), "img", NULL)) - ret_val = FALSE; - } - - return ret_val; -} - -static gboolean -delete_hidden_space (EHTMLEditorView *view) -{ - gint citation_level; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker, *block; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - if (!selection_start_marker || !selection_end_marker) - return FALSE; - - block = WEBKIT_DOM_ELEMENT (e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker))); - - citation_level = get_citation_level ( - WEBKIT_DOM_NODE (selection_start_marker), FALSE); - - if (selection_start_marker && citation_level > 0) { - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMNode *node; - WebKitDOMDocumentFragment *fragment; - - node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)); - if (!(WEBKIT_DOM_IS_ELEMENT (node) && - element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-quoted"))) - return FALSE; - - node = webkit_dom_node_get_previous_sibling (node); - if (!(WEBKIT_DOM_IS_ELEMENT (node) && - element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br"))) - return FALSE; - - node = webkit_dom_node_get_previous_sibling (node); - if (!(WEBKIT_DOM_IS_ELEMENT (node) && - webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), "data-hidden-space"))) - return FALSE; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_DELETE; - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - - remove_node (node); - - wrap_and_quote_element (view, block); - - fragment = webkit_dom_document_create_document_fragment (document); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - webkit_dom_document_create_text_node (document, " ")), - NULL); - ev->data.fragment = fragment; - - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - - e_html_editor_view_insert_new_history_event (view, ev); - - return TRUE; - } - - return FALSE; -} - -static gboolean -move_quoted_block_level_up (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *ev = NULL; - EHTMLEditorSelection *selection; - gint citation_level, success = FALSE; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *block; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - if (!selection_start_marker || !selection_end_marker) - return FALSE; - - block = e_html_editor_get_parent_block_node_from_child (WEBKIT_DOM_NODE (selection_start_marker)); - - citation_level = get_citation_level ( - WEBKIT_DOM_NODE (selection_start_marker), FALSE); - - if (selection_start_marker && citation_level > 0) { - if (webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (block), ".-x-evo-quoted", NULL)) { - - WebKitDOMNode *prev_sibling; - - webkit_dom_node_normalize (block); - - prev_sibling = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_start_marker)); - - if (!prev_sibling) { - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)); - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) - prev_sibling = webkit_dom_node_get_previous_sibling (parent); - } - - if (WEBKIT_DOM_IS_ELEMENT (prev_sibling)) - success = element_has_class ( - WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted"); - - /* We really have to be in the beginning of paragraph and - * not on the beginning of some line in the paragraph */ - if (success && webkit_dom_node_get_previous_sibling (prev_sibling)) - success = FALSE; - } - - if (view->priv->html_mode) - success = WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT ( - webkit_dom_node_get_parent_element (block)); - } - - if (success && !view->priv->undo_redo_in_progress) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_UNQUOTE; - - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - ev->data.dom.from = webkit_dom_node_clone_node (block, TRUE); - } - - if (success && citation_level == 1) { - gchar *inner_html; - WebKitDOMElement *paragraph, *element; - - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (block)); - webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (block), "-x-evo-to-remove"); - - paragraph = insert_new_line_into_citation (view, inner_html); - g_free (inner_html); - - if (paragraph) { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (paragraph), - WEBKIT_DOM_NODE (selection_start_marker), - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (paragraph)), - NULL); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (paragraph), - WEBKIT_DOM_NODE (selection_end_marker), - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (paragraph)), - NULL); - - remove_quoting_from_element (paragraph); - remove_wrapping_from_element (paragraph); - - /* Moving PRE block from citation to body */ - if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block)) { - WebKitDOMElement *pre; - WebKitDOMNode *child; - - pre = webkit_dom_document_create_element (document, "pre", NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (paragraph)), - WEBKIT_DOM_NODE (pre), - WEBKIT_DOM_NODE (paragraph), - NULL); - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph)))) - webkit_dom_node_append_child (WEBKIT_DOM_NODE (pre), child, NULL); - - remove_node (WEBKIT_DOM_NODE (paragraph)); - paragraph = pre; - } - - } - - if (block) - remove_node (block); - - while ((element = webkit_dom_document_get_element_by_id (document, "-x-evo-to-remove"))) - remove_node (WEBKIT_DOM_NODE (element)); - - if (paragraph) - remove_node_if_empty ( - webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (paragraph))); - } - - if (success && citation_level > 1) { - WebKitDOMNode *parent; - - if (view->priv->html_mode) { - webkit_dom_node_insert_before ( - block, - WEBKIT_DOM_NODE (selection_start_marker), - webkit_dom_node_get_first_child (block), - NULL); - webkit_dom_node_insert_before ( - block, - WEBKIT_DOM_NODE (selection_end_marker), - webkit_dom_node_get_first_child (block), - NULL); - } - - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block)); - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block)); - - parent = webkit_dom_node_get_parent_node (block); - - if (!webkit_dom_node_get_previous_sibling (block)) { - /* Currect block is in the beginning of citation, just move it - * before the citation where already is */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - block, - parent, - NULL); - } else if (!webkit_dom_node_get_next_sibling (block)) { - /* Currect block is at the end of the citation, just move it - * after the citation where already is */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - block, - webkit_dom_node_get_next_sibling (parent), - NULL); - } else { - /* Current block is somewhere in the middle of the citation - * so we need to split the citation and insert the block into - * the citation that is one level lower */ - WebKitDOMNode *clone, *child; - - clone = webkit_dom_node_clone_node (parent, FALSE); - - /* Move nodes that are after the currect block into the - * new blockquote */ - child = webkit_dom_node_get_next_sibling (block); - while (child) { - WebKitDOMNode *next = webkit_dom_node_get_next_sibling (child); - webkit_dom_node_append_child (clone, child, NULL); - child = next; - } - - clone = webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - clone, - webkit_dom_node_get_next_sibling (parent), - NULL); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - block, - clone, - NULL); - } - - wrap_and_quote_element (view, WEBKIT_DOM_ELEMENT (block)); - } - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - - e_html_editor_view_insert_new_history_event (view, ev); - } - - return success; -} - -static void -save_history_for_delete_or_backspace (EHTMLEditorView *view, - gboolean delete_key, - gboolean control_key) -{ - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment = NULL; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (!webkit_dom_dom_selection_get_range_count (dom_selection)) { - g_object_unref (dom_selection); - return; - } - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - - /* Check if we can delete something */ - if (webkit_dom_range_get_collapsed (range, NULL)) { - WebKitDOMRange *tmp_range; - - webkit_dom_dom_selection_modify ( - dom_selection, "move", delete_key ? "right" : "left", "character"); - - tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - if (webkit_dom_range_compare_boundary_points (tmp_range, 2 /* END_TO_END */, range, NULL) == 0) { - g_object_unref (dom_selection); - g_object_unref (range); - g_object_unref (tmp_range); - - return; - } - - webkit_dom_dom_selection_modify ( - dom_selection, "move", delete_key ? "left" : "right", "character"); - } - - if (save_history_before_event_in_table (view, range)) { - g_object_unref (range); - g_object_unref (dom_selection); - return; - } - - selection = e_html_editor_view_get_selection (view); - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_DELETE; - - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - g_object_unref (range); - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - - if (webkit_dom_range_get_collapsed (range, NULL)) { - gboolean removing_from_anchor = FALSE; - WebKitDOMRange *range_clone; - WebKitDOMNode *node, *next_block = NULL; - - block_selection_changed_callbacks (view); - - range_clone = webkit_dom_range_clone_range (range, NULL); - if (control_key) { - WebKitDOMRange *tmp_range; - - /* Control + Delete/Backspace deletes previous/next word. */ - webkit_dom_dom_selection_modify ( - dom_selection, "move", delete_key ? "right" : "left", "word"); - tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - if (delete_key) - webkit_dom_range_set_end ( - range_clone, - webkit_dom_range_get_end_container (tmp_range, NULL), - webkit_dom_range_get_end_offset (tmp_range, NULL), - NULL); - else - webkit_dom_range_set_start ( - range_clone, - webkit_dom_range_get_start_container (tmp_range, NULL), - webkit_dom_range_get_start_offset (tmp_range, NULL), - NULL); - g_object_unref (tmp_range); - } else { - typedef WebKitDOMNode * (*GetSibling)(WebKitDOMNode *node); - WebKitDOMNode *container, *sibling; - WebKitDOMElement *selection_marker; - - GetSibling get_sibling = delete_key ? - webkit_dom_node_get_next_sibling : - webkit_dom_node_get_previous_sibling; - - container = webkit_dom_range_get_end_container (range_clone, NULL); - sibling = get_sibling (container); - - selection_marker = webkit_dom_document_get_element_by_id ( - document, - delete_key ? - "-x-evo-selection-end-marker" : - "-x-evo-selection-start-marker"); - - if (selection_marker) { - WebKitDOMNode *tmp_sibling; - - tmp_sibling = get_sibling (WEBKIT_DOM_NODE (selection_marker)); - if (!tmp_sibling || (WEBKIT_DOM_IS_HTMLBR_ELEMENT (tmp_sibling) && - !element_has_class (WEBKIT_DOM_ELEMENT (tmp_sibling), "-x-evo-wrap-br"))) - sibling = WEBKIT_DOM_NODE (selection_marker); - } - - if (e_html_editor_node_is_selection_position_node (sibling)) { - if ((node = get_sibling (sibling))) - node = get_sibling (node); - if (node) { - if (WEBKIT_DOM_IS_ELEMENT (node) && - webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), "data-hidden-space")) { - fragment = webkit_dom_document_create_document_fragment (document); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - webkit_dom_document_create_text_node (document, " ")), - NULL); - } else if (delete_key) { - webkit_dom_range_set_start ( - range_clone, node, 0, NULL); - webkit_dom_range_set_end ( - range_clone, node, 1, NULL); - } - } else { - WebKitDOMRange *tmp_range, *actual_range; - - actual_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - - webkit_dom_dom_selection_modify ( - dom_selection, "move", delete_key ? "right" : "left", "character"); - - tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - if (webkit_dom_range_compare_boundary_points (tmp_range, 2 /* END_TO_END */, actual_range, NULL) != 0) { - WebKitDOMNode *actual_block; - WebKitDOMNode *tmp_block; - - actual_block = e_html_editor_get_parent_block_node_from_child (container); - - tmp_block = delete_key ? - webkit_dom_range_get_end_container (tmp_range, NULL) : - webkit_dom_range_get_start_container (tmp_range, NULL); - tmp_block = e_html_editor_get_parent_block_node_from_child (tmp_block); - - webkit_dom_dom_selection_modify ( - dom_selection, "move", delete_key ? "left" : "right", "character"); - - if (tmp_block) { - fragment = webkit_dom_document_create_document_fragment (document); - if (delete_key) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (actual_block, TRUE), - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (tmp_block, TRUE), - NULL); - if (delete_key) - next_block = tmp_block; - } else { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (tmp_block, TRUE), - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (actual_block, TRUE), - NULL); - } - g_object_set_data ( - G_OBJECT (fragment), - "history-concatenating-blocks", - GINT_TO_POINTER (1)); - } - } - g_object_unref (tmp_range); - g_object_unref (actual_range); - } - } else { - glong offset; - - /* FIXME This code is wrong for unicode smileys. */ - offset = webkit_dom_range_get_start_offset (range_clone, NULL); - - if (delete_key) - webkit_dom_range_set_end ( - range_clone, container, offset + 1, NULL); - else - webkit_dom_range_set_start ( - range_clone, container, offset - 1, NULL); - - removing_from_anchor = WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT ( - webkit_dom_node_get_parent_node (container)); - } - } - - if (!fragment) - fragment = webkit_dom_range_clone_contents (range_clone, NULL); - if (removing_from_anchor) - g_object_set_data ( - G_OBJECT (fragment), - "history-removing-from-anchor", - GINT_TO_POINTER (1)); - node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); - if (!node) { - g_free (ev); - unblock_selection_changed_callbacks (view); - g_object_unref (range); - g_object_unref (range_clone); - g_object_unref (dom_selection); - g_warning ("History event was not saved for %s key", delete_key ? "Delete" : "Backspace"); - return; - } - - if (control_key) { - if (delete_key) { - ev->after.start.x = ev->before.start.x; - ev->after.start.y = ev->before.start.y; - ev->after.end.x = ev->before.end.x; - ev->after.end.y = ev->before.end.y; - - webkit_dom_range_collapse (range_clone, TRUE, NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range_clone); - } else { - gboolean selection_saved = FALSE; - WebKitDOMRange *tmp_range; - - if (webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker")) - selection_saved = TRUE; - - if (selection_saved) - e_html_editor_selection_restore (selection); - - tmp_range = webkit_dom_range_clone_range (range_clone, NULL); - /* Prepare the selection to the right position after - * delete and save it. */ - webkit_dom_range_collapse (range_clone, TRUE, NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range_clone); - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - /* Restore the selection where it was before the - * history event was saved. */ - webkit_dom_range_collapse (tmp_range, FALSE, NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, tmp_range); - g_object_unref (tmp_range); - - if (selection_saved) - e_html_editor_selection_save (selection); - } - } else { - gboolean selection_saved = FALSE; - - if (webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker")) - selection_saved = TRUE; - - if (selection_saved) - e_html_editor_selection_restore (selection); - - if (delete_key) { - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - } else { - webkit_dom_dom_selection_modify (dom_selection, "move", "left", "character"); - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - webkit_dom_dom_selection_modify (dom_selection, "move", "right", "character"); - - ev->after.end.x = ev->after.start.x; - ev->after.end.y = ev->after.start.y; - } - - if (selection_saved) - e_html_editor_selection_save (selection); - } - - g_object_unref (range_clone); - - if (delete_key) { - if (!WEBKIT_DOM_IS_ELEMENT (node)) { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, FALSE)), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), - NULL); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, TRUE)), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), - NULL); - } - } else { - if (!WEBKIT_DOM_IS_ELEMENT (node)) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, TRUE)), - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - create_selection_marker (document, FALSE)), - NULL); - } - } - - /* If concatenating two blocks with pressing Delete on the end - * of the previous one and the next node contain content that - * is wrapped on multiple lines, the last line will by separated - * by WebKit to the separate block. To avoid it let's remove - * all quoting and wrapping from the next paragraph. */ - if (next_block) { - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (next_block)); - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (next_block)); - } - - unblock_selection_changed_callbacks (view); - } else { - WebKitDOMElement *tmp_element; - WebKitDOMNode *sibling; - - ev->after.start.x = ev->before.start.x; - ev->after.start.y = ev->before.start.y; - ev->after.end.x = ev->before.start.x; - ev->after.end.y = ev->before.start.y; - - fragment = webkit_dom_range_clone_contents (range, NULL); - - tmp_element = webkit_dom_document_fragment_query_selector ( - fragment, "#-x-evo-selection-start-marker", NULL); - if (tmp_element) - remove_node (WEBKIT_DOM_NODE (tmp_element)); - - tmp_element = webkit_dom_document_fragment_query_selector ( - fragment, "#-x-evo-selection-end-marker", NULL); - if (tmp_element) - remove_node (WEBKIT_DOM_NODE (tmp_element)); - - /* If any empty blockquote is presented, remove it. */ - tmp_element = webkit_dom_document_query_selector ( - document, "blockquote[type=cite]:empty", NULL); - if (tmp_element) - remove_node (WEBKIT_DOM_NODE (tmp_element)); - - /* Selection starts in the beginning of blockquote. */ - tmp_element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element)); - if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-quoted")) { - WebKitDOMNode *child; - - tmp_element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - /* If there is no text after the selection end it means that - * the block will be replaced with block that is body's descendant - * and not the blockquote's one. Also if the selection started - * in the beginning of blockquote we have to insert the quote - * characters into the deleted content to correctly restore - * them during undo/redo operations. */ - if (!(tmp_element && webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element)))) { - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); - while (child && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child)) - child = webkit_dom_node_get_first_child (child); - - child = webkit_dom_node_get_first_child (child); - if (child && (WEBKIT_DOM_IS_TEXT (child) || - (WEBKIT_DOM_IS_ELEMENT (child) && - !element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-quoted")))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (child), - webkit_dom_node_clone_node (sibling, TRUE), - child, - NULL); - } - } - } - - /* When we were cloning the range above and the range contained - * quoted content there will still be blockquote missing in the - * final range. Let's modify the fragment and add it there. */ - tmp_element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - if (tmp_element) { - WebKitDOMNode *node; - - node = WEBKIT_DOM_NODE (tmp_element); - while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node))) - node = webkit_dom_node_get_parent_node (node); - - if (node && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) { - WebKitDOMNode *last_child; - - last_child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)); - - if (last_child && !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child)) { - WebKitDOMDocumentFragment *tmp_fragment; - WebKitDOMNode *clone; - - tmp_fragment = webkit_dom_document_create_document_fragment (document); - clone = webkit_dom_node_clone_node (node, FALSE); - clone = webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (tmp_fragment), clone, NULL); - webkit_dom_node_append_child (clone, WEBKIT_DOM_NODE (fragment), NULL); - fragment = tmp_fragment; - } - } - } - - /* FIXME Ugly hack */ - /* If the deleted selection contained the signature (or at least its - * part) replace it with the unchanged signature to correctly perform - * undo operation. */ - tmp_element = webkit_dom_document_fragment_query_selector (fragment, ".-x-evo-signature-wrapper", NULL); - if (tmp_element) { - WebKitDOMElement *signature; - - signature = webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL); - if (signature) { - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp_element)), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (signature), TRUE), - WEBKIT_DOM_NODE (tmp_element), - NULL); - } - } - } - - g_object_unref (range); - g_object_unref (dom_selection); - - g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (delete_key)); - g_object_set_data (G_OBJECT (fragment), "history-control-key", GINT_TO_POINTER (control_key)); - - ev->data.fragment = fragment; - e_html_editor_view_insert_new_history_event (view, ev); -} - -static gboolean -fix_structure_after_delete_before_quoted_content (EHTMLEditorView *view, - GdkEventKey *event, - gboolean delete_key) -{ - EHTMLEditorSelection *selection; - gboolean collapsed = FALSE; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker; - WebKitDOMNode *block, *node; - - selection = e_html_editor_view_get_selection (view); - - collapsed = e_html_editor_selection_is_collapsed (selection); - - e_html_editor_selection_save (selection); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - selection_start_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-start-marker", NULL); - selection_end_marker = webkit_dom_document_query_selector ( - document, "span#-x-evo-selection-end-marker", NULL); - - if (!selection_start_marker || !selection_end_marker) - return FALSE; - - if (collapsed) { - WebKitDOMNode *next_block; - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - next_block = webkit_dom_node_get_next_sibling (block); - - /* Next block is quoted content */ - if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_block)) - goto restore; - - /* Delete was pressed in block without any content */ - if (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker))) - goto restore; - - /* If there is just BR element go ahead */ - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)); - if (node && !WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) - goto restore; - else { - if (event) - save_history_for_delete_or_backspace ( - view, event->keyval == GDK_KEY_Delete, ((event)->state & GDK_CONTROL_MASK)); - - /* Remove the empty block and move caret to the right place. */ - remove_node (block); - - if (delete_key) { - /* To the beginning of the next block. */ - e_html_editor_selection_move_caret_into_element ( - document, WEBKIT_DOM_ELEMENT (next_block), TRUE); - } else { - WebKitDOMNode *prev_block; - - /* On the end of previous block. */ - prev_block = webkit_dom_node_get_previous_sibling (next_block); - while (prev_block && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block)) - prev_block = webkit_dom_node_get_last_child (prev_block); - - if (prev_block) - e_html_editor_selection_move_caret_into_element ( - document, - WEBKIT_DOM_ELEMENT (prev_block), - FALSE); - } - - return TRUE; - } - } else { - WebKitDOMNode *end_block, *parent; - - /* Let the quote marks be selectable to nearly correctly remove the - * selection. Corrections after are done in body_keyup_event_cb. */ - enable_quote_marks_select (document); - - parent = webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)); - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) || - element_has_tag (WEBKIT_DOM_ELEMENT (parent), "b") || - element_has_tag (WEBKIT_DOM_ELEMENT (parent), "i") || - element_has_tag (WEBKIT_DOM_ELEMENT (parent), "u")) - node = webkit_dom_node_get_previous_sibling (parent); - else - node = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_start_marker)); - - if (!node || !WEBKIT_DOM_IS_ELEMENT (node)) - goto restore; - - if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-quoted")) - goto restore; - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - end_block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_end_marker)); - - /* Situation where the start of the selection is in the beginning - * of the block in quoted content and the end in the beginning of - * content that is after the citation or the selection end is in - * the end of the quoted content (showed by ^). We have to - * mark the start block to correctly restore the structure - * afterwards. - * - * > |xxx - * > xxx^ - * |xxx - * */ - if (get_citation_level (end_block, FALSE) > 0) { - WebKitDOMNode *parent; - - if (webkit_dom_node_get_next_sibling (end_block)) - goto restore; - - parent = webkit_dom_node_get_parent_node (end_block); - while (parent && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent)) { - WebKitDOMNode *next_parent = webkit_dom_node_get_parent_node (parent); - - if (webkit_dom_node_get_next_sibling (parent) && - !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (next_parent)) - goto restore; - - parent = next_parent; - } - } - } - restore: - if (event) - save_history_for_delete_or_backspace ( - view, event->keyval == GDK_KEY_Delete, ((event)->state & GDK_CONTROL_MASK)); - - e_html_editor_selection_restore (selection); - - return FALSE; -} - -static gboolean -split_citation (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMElement *element; - - selection = e_html_editor_view_get_selection (view); - - if (!view->priv->undo_redo_in_progress) { - WebKitDOMDocument *document; - WebKitDOMElement *selection_end; - WebKitDOMNode *sibling; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_CITATION_SPLIT; - - e_html_editor_selection_save (selection); - - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); - - if (!e_html_editor_selection_is_collapsed (selection)) { - WebKitDOMRange *tmp_range; - - tmp_range = html_editor_view_get_dom_range (view); - insert_delete_event (view, tmp_range); - g_object_unref (tmp_range); - - ev->before.end.x = ev->before.start.x; - ev->before.end.y = ev->before.start.y; - } - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - selection_end = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end)); - if (!sibling || (WEBKIT_DOM_IS_HTMLBR_ELEMENT (sibling) && - !element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-wrap-br"))) { - WebKitDOMDocumentFragment *fragment; - - fragment = webkit_dom_document_create_document_fragment (document); - ev->data.fragment = fragment; - } else { - ev->data.fragment = NULL; - } - - e_html_editor_selection_restore (selection); - } - - element = insert_new_line_into_citation (view, ""); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); - - e_html_editor_view_insert_new_history_event (view, ev); - } - - return element != NULL; -} - -static gboolean -selection_is_in_table (WebKitDOMDocument *document, - gboolean *first_cell, - WebKitDOMNode **table_node) -{ - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMNode *node, *parent; - WebKitDOMRange *range; - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (first_cell != NULL) - *first_cell = FALSE; - - if (table_node != NULL) - *table_node = NULL; - - if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { - g_object_unref (dom_selection); - return FALSE; - } - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - node = webkit_dom_range_get_start_container (range, NULL); - g_object_unref (range); - g_object_unref (dom_selection); - - parent = node; - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent)) { - if (first_cell != NULL) { - if (!webkit_dom_node_get_previous_sibling (parent)) { - gboolean on_start = TRUE; - WebKitDOMNode *tmp; - - tmp = webkit_dom_node_get_previous_sibling (node); - if (!tmp && WEBKIT_DOM_IS_TEXT (node)) - on_start = webkit_dom_range_get_start_offset (range, NULL) == 0; - else if (tmp) - on_start = FALSE; - - if (on_start) { - node = webkit_dom_node_get_parent_node (parent); - if (node && WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (node)) - if (!webkit_dom_node_get_previous_sibling (node)) - *first_cell = TRUE; - } - } - } else - return TRUE; - } - if (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (parent)) { - if (table_node != NULL) - *table_node = parent; - else - return TRUE; - } - parent = webkit_dom_node_get_parent_node (parent); - } - - if (table_node == NULL) - return FALSE; - - return *table_node != NULL; -} - -static gboolean -jump_to_next_table_cell (EHTMLEditorView *view, - gboolean jump_back) -{ - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMNode *node, *cell; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - if (!selection_is_in_table (document, NULL, NULL)) - return FALSE; - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - node = webkit_dom_range_get_start_container (range, NULL); - - cell = node; - while (cell && !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell)) { - cell = webkit_dom_node_get_parent_node (cell); - } - - if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell)) { - g_object_unref (range); - g_object_unref (dom_selection); - return FALSE; - } - - if (jump_back) { - /* Get previous cell */ - node = webkit_dom_node_get_previous_sibling (cell); - if (!node || !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) { - /* No cell, go one row up. */ - node = webkit_dom_node_get_parent_node (cell); - node = webkit_dom_node_get_previous_sibling (node); - if (node && WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (node)) { - node = webkit_dom_node_get_last_child (node); - } else { - /* No row above, move to the block before table. */ - node = webkit_dom_node_get_parent_node (cell); - while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node))) - node = webkit_dom_node_get_parent_node (node); - - node = webkit_dom_node_get_previous_sibling (node); - } - } - } else { - /* Get next cell */ - node = webkit_dom_node_get_next_sibling (cell); - if (!node || !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) { - /* No cell, go one row below. */ - node = webkit_dom_node_get_parent_node (cell); - node = webkit_dom_node_get_next_sibling (node); - if (node && WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (node)) { - node = webkit_dom_node_get_first_child (node); - } else { - /* No row below, move to the block after table. */ - node = webkit_dom_node_get_parent_node (cell); - while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node))) - node = webkit_dom_node_get_parent_node (node); - - node = webkit_dom_node_get_next_sibling (node); - } - } - } - - if (!node) - return FALSE; - - webkit_dom_range_select_node_contents (range, node, NULL); - webkit_dom_range_collapse (range, TRUE, NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - g_object_unref (dom_selection); - - return TRUE; -} - -static gboolean -delete_last_character_from_previous_line_in_quoted_block (EHTMLEditorView *view, - GdkEventKey *event) -{ - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev = NULL; - gboolean hidden_space = FALSE; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment = NULL; - WebKitDOMElement *element; - WebKitDOMNode *node, *beginning, *prev_sibling; - - selection = e_html_editor_view_get_selection (view); - - /* We have to be in quoted content. */ - if (!e_html_editor_selection_is_citation (selection)) - return FALSE; - - /* Selection is just caret. */ - if (!e_html_editor_selection_is_collapsed (selection)) - return FALSE; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - e_html_editor_selection_save (selection); - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - /* Before the caret are just quote characters */ - beginning = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - if (!(beginning && WEBKIT_DOM_IS_ELEMENT (beginning))) { - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) - beginning = webkit_dom_node_get_previous_sibling (parent); - else - goto out; - } - - /* Before the text is the beginning of line. */ - if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning), "-x-evo-quoted"))) - goto out; - - /* If we are just on the beginning of the line and not on the beginning of - * the block we need to remove the last character ourselves as well, otherwise - * WebKit will put the caret to wrong position. */ - if (!(prev_sibling = webkit_dom_node_get_previous_sibling (beginning))) - goto out; - - if (event) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_DELETE; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - fragment = webkit_dom_document_create_document_fragment (document); - } - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) { - if (event) - webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment), prev_sibling, NULL); - else - remove_node (prev_sibling); - } - - prev_sibling = webkit_dom_node_get_previous_sibling (beginning); - if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && - webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (prev_sibling), "data-hidden-space")) { - hidden_space = TRUE; - if (event) - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (fragment), - prev_sibling, - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), - NULL); - else - remove_node (prev_sibling); - } - - node = webkit_dom_node_get_previous_sibling (beginning); - - if (event) - webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment), beginning, NULL); - else - remove_node (beginning); - - if (!hidden_space) { - if (event) { - gchar *data; - - data = webkit_dom_character_data_substring_data ( - WEBKIT_DOM_CHARACTER_DATA (node), - webkit_dom_character_data_get_length ( - WEBKIT_DOM_CHARACTER_DATA (node)) -1, - 1, - NULL); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE ( - webkit_dom_document_create_text_node (document, data)), - NULL); - - g_free (data); - } - - webkit_dom_character_data_delete_data ( - WEBKIT_DOM_CHARACTER_DATA (node), - webkit_dom_character_data_get_length ( - WEBKIT_DOM_CHARACTER_DATA (node)) -1, - 1, - NULL); - } - - if (event) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - - ev->data.fragment = fragment; - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_selection_restore (selection); - - return TRUE; - out: - e_html_editor_selection_restore (selection); - - return FALSE; -} - -static gboolean -delete_last_character_on_line_in_quoted_block (EHTMLEditorView *view, - GdkEventKey *event) -{ - EHTMLEditorSelection *selection; - gboolean success = FALSE; - WebKitDOMDocument *document; - WebKitDOMElement *element; - WebKitDOMNode *node, *beginning, *next_sibling; - - selection = e_html_editor_view_get_selection (view); - - /* We have to be in quoted content. */ - if (!e_html_editor_selection_is_citation (selection)) - return FALSE; - - /* Selection is just caret. */ - if (!e_html_editor_selection_is_collapsed (selection)) - return FALSE; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - e_html_editor_selection_save (selection); - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - /* selection end marker */ - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - - /* We have to be on the end of line. */ - next_sibling = webkit_dom_node_get_next_sibling (node); - if (next_sibling && - (!WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling) || - webkit_dom_node_get_next_sibling (next_sibling))) - goto out; - - /* Before the caret is just text. */ - node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - if (!(node && WEBKIT_DOM_IS_TEXT (node))) - goto out; - - /* There is just one character. */ - if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)) != 1) - goto out; - - beginning = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (node)); - if (!(beginning && WEBKIT_DOM_IS_ELEMENT (beginning))) - goto out; - - /* Before the text is the beginning of line. */ - if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning), "-x-evo-quoted"))) - goto out; - - if (!webkit_dom_node_get_previous_sibling (beginning)) - goto out; - - if (event) - save_history_for_delete_or_backspace ( - view, event->keyval == GDK_KEY_Delete, ((event)->state & GDK_CONTROL_MASK)); - - element = webkit_dom_node_get_parent_element (beginning); - remove_node (WEBKIT_DOM_NODE (element)); - - success = TRUE; - out: - e_html_editor_selection_restore (selection); - - if (success) - insert_new_line_into_citation (view, NULL); - - return success; -} - -static void -remove_history_event (EHTMLEditorView *view, - GList *item) -{ - free_history_event_content (item->data); - - view->priv->history = g_list_delete_link (view->priv->history, item); - view->priv->history_size--; -} - -static gboolean -insert_tabulator (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *ev = NULL; - EHTMLEditorSelection *selection; - gboolean success; - - selection = e_html_editor_view_get_selection (view); - - if (!view->priv->undo_redo_in_progress) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_INPUT; - - if (!e_html_editor_selection_is_collapsed (selection)) { - WebKitDOMRange *tmp_range; - - tmp_range = html_editor_view_get_dom_range (view); - insert_delete_event (view, tmp_range); - g_object_unref (tmp_range); - } - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - ev->before.end.x = ev->before.start.x; - ev->before.end.y = ev->before.start.y; - } - - success = e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "\t"); - - if (ev) { - if (success) { - WebKitDOMDocument *document; - WebKitDOMElement *element; - WebKitDOMDocumentFragment *fragment; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - fragment = webkit_dom_document_create_document_fragment (document); - element = webkit_dom_document_create_element (document, "span", NULL); - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (element), "\t", NULL); - webkit_dom_element_set_attribute ( - element, "class", "Apple-tab-span", NULL); - webkit_dom_element_set_attribute ( - element, "style", "white-space:pre", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), WEBKIT_DOM_NODE (element), NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE (create_selection_marker (document, TRUE)), - NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - WEBKIT_DOM_NODE (create_selection_marker (document, FALSE)), - NULL); - ev->data.fragment = fragment; - - e_html_editor_view_insert_new_history_event (view, ev); - e_html_editor_view_set_changed (view, TRUE); - } else { - remove_history_event (view, view->priv->history); - remove_history_event (view, view->priv->history); - g_free (ev); - } - } - - return success; -} - -static gboolean -selection_is_in_empty_list_item (WebKitDOMNode *selection_start_marker) -{ - gchar *text; - WebKitDOMNode *sibling; - - /* Selection needs to be collapsed. */ - sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker)); - if (!e_html_editor_node_is_selection_position_node (sibling)) - return FALSE; - - /* After the selection end there could be just the BR element. */ - sibling = webkit_dom_node_get_next_sibling (sibling); - if (sibling && !WEBKIT_DOM_IS_HTMLBR_ELEMENT (sibling)) - return FALSE; - - if (sibling && webkit_dom_node_get_next_sibling (sibling)) - return FALSE; - - sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)); - - /* Only text node with the zero width space character is allowed. */ - if (!WEBKIT_DOM_IS_TEXT (sibling)) - return FALSE; - - if (webkit_dom_node_get_previous_sibling (sibling)) - return FALSE; - - if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (sibling)) != 1) - return FALSE; - - text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (sibling)); - if (!(text && g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0)) { - g_free (text); - return FALSE; - } - - g_free (text); - - return TRUE; -} - -static gboolean -return_pressed_in_image_wrapper (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - WebKitDOMElement *selection_start_marker; - WebKitDOMNode *parent, *block, *clone; - - selection = e_html_editor_view_get_selection (view); - if (!e_html_editor_selection_is_collapsed (selection)) - return FALSE; - - e_html_editor_selection_save (selection); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); - if (!element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-resizable-wrapper")) { - e_html_editor_selection_restore (selection); - return FALSE; - } - - if (!view->priv->undo_redo_in_progress) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_INPUT; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - fragment = webkit_dom_document_create_document_fragment (document); - - g_object_set_data ( - G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1)); - } - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - clone = webkit_dom_node_clone_node (block, FALSE); - webkit_dom_node_append_child ( - clone, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), NULL); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - clone, - block, - NULL); - - if (ev) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (clone, TRUE), - NULL); - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - - ev->data.fragment = fragment; - - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_view_set_changed (view, TRUE); - - e_html_editor_selection_restore (selection); - - return TRUE; -} - -static gboolean -return_pressed_in_empty_list_item (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker; - WebKitDOMNode *parent; - - selection = e_html_editor_view_get_selection (view); - if (!e_html_editor_selection_is_collapsed (selection)) - return FALSE; - - e_html_editor_selection_save (selection); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); - if (!WEBKIT_DOM_IS_HTMLLI_ELEMENT (parent)) { - e_html_editor_selection_restore (selection); - return FALSE; - } - - if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start_marker))) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMDocumentFragment *fragment; - WebKitDOMElement *paragraph; - WebKitDOMNode *list; - - if (!view->priv->undo_redo_in_progress) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_INPUT; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - fragment = webkit_dom_document_create_document_fragment (document); - - g_object_set_data ( - G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1)); - } - - list = split_node_into_two (parent, -1); - - if (ev) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - parent, - NULL); - } else { - remove_node (parent); - } - - paragraph = prepare_paragraph (selection, document, TRUE); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (list), - WEBKIT_DOM_NODE (paragraph), - list, - NULL); - - remove_node_if_empty (list); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - - ev->data.fragment = fragment; - - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_selection_restore (selection); - - e_html_editor_view_set_changed (view, TRUE); - - return TRUE; - } - - e_html_editor_selection_restore (selection); - - return FALSE; -} - -static void -process_smiley_on_delete_or_backspace (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMElement *element; - WebKitDOMNode *parent; - gboolean in_smiley = FALSE; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - e_html_editor_selection_save (view->priv->selection); - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - if (WEBKIT_DOM_IS_ELEMENT (parent) && - element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text")) - in_smiley = TRUE; - else { - if (e_html_editor_selection_is_collapsed (view->priv->selection)) { - WebKitDOMNode *prev_sibling; - - prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) { - gchar *text = webkit_dom_character_data_get_data ( - WEBKIT_DOM_CHARACTER_DATA (prev_sibling)); - - if (g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0) { - WebKitDOMNode *prev_prev_sibling; - - prev_prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); - if (WEBKIT_DOM_IS_ELEMENT (prev_prev_sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (prev_prev_sibling), "-x-evo-smiley-wrapper")) { - remove_node (prev_sibling); - in_smiley = TRUE; - parent = webkit_dom_node_get_last_child (prev_prev_sibling); - } - } - - g_free (text); - } - } else { - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - if (WEBKIT_DOM_IS_ELEMENT (parent) && - element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text")) - in_smiley = TRUE; - } - } - - if (in_smiley) { - WebKitDOMNode *wrapper; - - wrapper = webkit_dom_node_get_parent_node (parent); - if (!view->priv->html_mode) { - WebKitDOMNode *child; - - while ((child = webkit_dom_node_get_first_child (parent))) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (wrapper), - child, - wrapper, - NULL); - } - /* In the HTML mode the whole smiley will be removed. */ - remove_node (wrapper); - /* FIXME history will be probably broken here */ - } - - e_html_editor_selection_restore (view->priv->selection); -} - -static gboolean -key_press_event_process_return_key (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - gboolean first_cell = FALSE; - WebKitDOMDocument *document; - WebKitDOMNode *table = NULL; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - /* Return pressed in the beginning of the first cell will insert - * new block before the table (and move the caret there) if none - * is already there, otherwise it will act as normal return. */ - if (selection_is_in_table (document, &first_cell, &table) && first_cell) { - WebKitDOMNode *node; - - node = webkit_dom_node_get_previous_sibling (table); - if (!node) { - node = webkit_dom_node_get_next_sibling (table); - node = webkit_dom_node_clone_node (node, FALSE); - webkit_dom_node_append_child ( - node, - WEBKIT_DOM_NODE (webkit_dom_document_create_element ( - document, "br", NULL)), - NULL); - add_selection_markers_into_element_start ( - document, WEBKIT_DOM_ELEMENT (node), NULL, NULL); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (table), - node, - table, - NULL); - e_html_editor_selection_restore (selection); - e_html_editor_view_set_changed (view, TRUE); - return TRUE; - } - } - - /* When user presses ENTER in a citation block, WebKit does - * not break the citation automatically, so we need to use - * the special command to do it. */ - if (e_html_editor_selection_is_citation (selection)) { - e_html_editor_view_remove_input_event_listener_from_body (view); - if (split_citation (view)) { - WebKitDOMRange *range; - - view->priv->return_key_pressed = TRUE; - range = html_editor_view_get_dom_range (view); - html_editor_view_check_magic_links (view, range, FALSE); - view->priv->return_key_pressed = FALSE; - g_object_unref (range); - e_html_editor_view_set_changed (view, TRUE); - - return TRUE; - } - return FALSE; - } - - /* If the ENTER key is pressed inside an empty list item then the list - * is broken into two and empty paragraph is inserted between lists. */ - if (return_pressed_in_empty_list_item (view)) - return TRUE; - - if (return_pressed_in_image_wrapper (view)) - return TRUE; - - return FALSE; -} - -static gboolean -remove_empty_bulleted_list_item (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start; - WebKitDOMNode *parent; - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_save (selection); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - selection_start = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start)); - while (parent && !node_is_list_or_item (parent)) - parent = webkit_dom_node_get_parent_node (parent); - - if (!parent) - goto out; - - if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start))) { - EHTMLEditorViewHistoryEvent *ev = NULL; - WebKitDOMDocumentFragment *fragment; - WebKitDOMNode *prev_item; - - prev_item = webkit_dom_node_get_previous_sibling (parent); - - if (!view->priv->undo_redo_in_progress) { - /* Insert new history event for Return to have the right coordinates. - * The fragment will be added later. */ - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_DELETE; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - - fragment = webkit_dom_document_create_document_fragment (document); - } - - if (ev) { - if (prev_item) - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - webkit_dom_node_clone_node (prev_item, TRUE), - NULL); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (fragment), - parent, - NULL); - } else - remove_node (parent); - - if (prev_item) - add_selection_markers_into_element_end ( - document, WEBKIT_DOM_ELEMENT (prev_item), NULL, NULL); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - - ev->data.fragment = fragment; - - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_view_set_changed (view, TRUE); - e_html_editor_selection_restore (selection); - - return TRUE; - } - out: - e_html_editor_selection_restore (selection); - - return FALSE; -} - -static gboolean -key_press_event_process_backspace_key (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - - selection = e_html_editor_view_get_selection (view); - - /* BackSpace pressed in the beginning of quoted content changes - * format to normal and inserts text into body */ - if (e_html_editor_selection_is_collapsed (selection)) { - e_html_editor_selection_save (selection); - if (move_quoted_block_level_up (view) || delete_hidden_space (view)) { - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_for_current_paragraph (view); - e_html_editor_view_set_changed (view, TRUE); - return TRUE; - } - e_html_editor_selection_restore (selection); - } - - /* BackSpace in indented block decrease indent level by one */ - if (e_html_editor_selection_is_indented (selection) && - e_html_editor_selection_is_collapsed (selection)) { - WebKitDOMDocument *document; - WebKitDOMElement *selection_start; - WebKitDOMNode *prev_sibling; - - e_html_editor_selection_save (selection); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - selection_start = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - /* Empty text node before caret */ - prev_sibling = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_start)); - if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) - if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (prev_sibling)) == 0) - prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); - - e_html_editor_selection_restore (selection); - if (!prev_sibling) { - e_html_editor_selection_unindent (selection); - e_html_editor_view_set_changed (view, TRUE); - return TRUE; - } - } - - /* BackSpace pressed in an empty item in the bulleted list removes it. */ - if (!view->priv->html_mode && e_html_editor_selection_is_collapsed (selection) && - remove_empty_bulleted_list_item (view)) - return TRUE; - - - if (prevent_from_deleting_last_element_in_body (view)) - return TRUE; - - return FALSE; -} - -static gboolean -key_press_event_process_delete_or_backspace_key (EHTMLEditorView *view, - gboolean delete, - GdkEventKey *event) -{ - gboolean local_delete; - - local_delete = (event && event->keyval == GDK_KEY_Delete) || delete; - - if (view->priv->magic_smileys) { - /* If deleting something in a smiley it won't be a smiley - * anymore (at least from Evolution' POV), so remove all - * the elements that are hidden in the wrapper and leave - * just the text. Also this ensures that when a smiley is - * recognized and we press the BackSpace key we won't delete - * the UNICODE_HIDDEN_SPACE, but we will correctly delete - * the last character of smiley. */ - process_smiley_on_delete_or_backspace (view); - } - - if (!local_delete && !view->priv->html_mode && - delete_last_character_on_line_in_quoted_block (view, event)) - goto out; - - if (!local_delete && !view->priv->html_mode && - delete_last_character_from_previous_line_in_quoted_block (view, event)) - goto out; - - if (fix_structure_after_delete_before_quoted_content (view, event, local_delete)) - goto out; - - if (local_delete) { - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker; - WebKitDOMNode *sibling, *block, *next_block; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - /* This needs to be performed just in plain text mode - * and when the selection is collapsed. */ - if (view->priv->html_mode || !e_html_editor_selection_is_collapsed (selection)) - return FALSE; - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - sibling = webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (selection_start_marker)); - /* Check if the key was pressed in the beginning of block. */ - if (!(sibling && WEBKIT_DOM_IS_ELEMENT (sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-quoted"))) { - e_html_editor_selection_restore (selection); - return FALSE; - } - - sibling = webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (selection_start_marker)); - sibling = webkit_dom_node_get_next_sibling (sibling); - - /* And also the current block was empty. */ - if (!(!sibling || (sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (sibling) && - !element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-wrap-br")))) { - e_html_editor_selection_restore (selection); - return FALSE; - } - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - next_block = webkit_dom_node_get_next_sibling (block); - - remove_node (block); - - e_html_editor_selection_move_caret_into_element ( - document, WEBKIT_DOM_ELEMENT (next_block), TRUE); - - goto out; - } else { - /* Concatenating a non-quoted block with Backspace key to the - * previous block that is inside a quoted content. */ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker; - WebKitDOMNode *node, *block, *prev_block, *last_child, *child; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (view->priv->html_mode || !e_html_editor_selection_is_collapsed (selection) || - e_html_editor_selection_is_citation (selection)) - return FALSE; - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)); - if (node) { - e_html_editor_selection_restore (selection); - return FALSE; - } - - remove_empty_blocks (document); - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - prev_block = webkit_dom_node_get_previous_sibling (block); - if (!prev_block || !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block)) { - e_html_editor_selection_restore (selection); - return FALSE; - } - - last_child = webkit_dom_node_get_last_child (prev_block); - while (last_child && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child)) - last_child = webkit_dom_node_get_last_child (last_child); - - if (!last_child) { - e_html_editor_selection_restore (selection); - return FALSE; - } - - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child)); - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child)); - - node = webkit_dom_node_get_last_child (last_child); - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) - remove_node (node); - - while ((child = webkit_dom_node_get_first_child (block))) - webkit_dom_node_append_child (last_child, child, NULL); - - remove_node (block); - - if (WEBKIT_DOM_IS_ELEMENT (last_child)) - wrap_and_quote_element (view, WEBKIT_DOM_ELEMENT (last_child)); - - e_html_editor_selection_restore (selection); - - goto out; - } - - return FALSE; - out: - e_html_editor_view_force_spell_check_for_current_paragraph (view); - e_html_editor_view_set_changed (view, TRUE); - - return TRUE; -} - -static gboolean -html_editor_view_key_press_event (GtkWidget *widget, - GdkEventKey *event) -{ - EHTMLEditorView *view = E_HTML_EDITOR_VIEW (widget); - - view->priv->dont_save_history_in_body_input = FALSE; - - view->priv->return_key_pressed = FALSE; - view->priv->space_key_pressed = FALSE; - - if (event->keyval == GDK_KEY_Menu) { - gboolean event_handled; - - g_signal_emit ( - widget, signals[POPUP_EVENT], - 0, event, &event_handled); - - return event_handled; - } - - if ((((event)->state & GDK_SHIFT_MASK) && - ((event)->keyval == GDK_KEY_Insert)) || - (((event)->state & GDK_CONTROL_MASK) && - ((event)->keyval == GDK_KEY_v))) { - g_signal_emit_by_name ( - WEBKIT_WEB_VIEW (view), "paste-clipboard"); - return TRUE; - } - - if (((event)->state & GDK_CONTROL_MASK) && - ((event)->keyval == GDK_KEY_Insert)) { - g_signal_emit_by_name ( - WEBKIT_WEB_VIEW (view), "copy-clipboard"); - return TRUE; - } - - if (((event)->state & GDK_CONTROL_MASK) && - ((event)->keyval == GDK_KEY_z)) { - e_html_editor_view_undo (view); - return TRUE; - } - - if (((event)->state & (GDK_CONTROL_MASK)) && - ((event)->keyval == GDK_KEY_Z)) { - e_html_editor_view_redo (view); - return TRUE; - } - - if (((event)->state & GDK_SHIFT_MASK) && - ((event)->keyval == GDK_KEY_Delete)) { - g_signal_emit_by_name ( - WEBKIT_WEB_VIEW (view), "cut-clipboard"); - return TRUE; - } - - if (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_ISO_Left_Tab) { - if (jump_to_next_table_cell (view, event->keyval == GDK_KEY_ISO_Left_Tab)) - return TRUE; - - if (event->keyval == GDK_KEY_Tab) - return insert_tabulator (view); - else - return FALSE; - } - - if (is_return_key (event) && key_press_event_process_return_key (view)) - return TRUE; - - if (event->keyval == GDK_KEY_BackSpace && - key_press_event_process_backspace_key (view)) { - return TRUE; - } - - if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_BackSpace) { - if (key_press_event_process_delete_or_backspace_key (view, event->keyval == GDK_KEY_Delete, event)) - return TRUE; - else if (!view->priv->html_mode) { - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), TRUE); - } - } - - /* Chain up to parent's key_press_event() method. */ - return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)-> - key_press_event (widget, event); -} - -static void -html_editor_view_paste_as_text (EHTMLEditorView *view) -{ - GtkClipboard *clipboard; - - clipboard = gtk_clipboard_get_for_display ( - gdk_display_get_default (), - GDK_SELECTION_CLIPBOARD); - - gtk_clipboard_request_text ( - clipboard, - (GtkClipboardTextReceivedFunc) clipboard_text_received_for_paste_as_text, - view); -} - -static void -html_editor_view_paste_clipboard_quoted (EHTMLEditorView *view) -{ - GtkClipboard *clipboard; - - clipboard = gtk_clipboard_get_for_display ( - gdk_display_get_default (), - GDK_SELECTION_CLIPBOARD); - - gtk_clipboard_request_text ( - clipboard, - (GtkClipboardTextReceivedFunc) clipboard_text_received, - view); -} - -static gboolean -html_editor_view_image_exists_in_cache (const gchar *image_uri) -{ - gchar *filename; - gchar *hash; - gboolean exists = FALSE; - - if (!emd_global_http_cache) - return FALSE; - - hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1); - filename = camel_data_cache_get_filename ( - emd_global_http_cache, "http", hash); - - if (filename != NULL) { - exists = g_file_test (filename, G_FILE_TEST_EXISTS); - g_free (filename); - } - - g_free (hash); - - return exists; -} - -static gchar * -html_editor_view_redirect_uri (EHTMLEditorView *view, - const gchar *uri) -{ - EImageLoadingPolicy image_policy; - GSettings *settings; - gboolean uri_is_http; - - uri_is_http = - g_str_has_prefix (uri, "http:") || - g_str_has_prefix (uri, "https:") || - g_str_has_prefix (uri, "evo-http:") || - g_str_has_prefix (uri, "evo-https:"); - - /* Redirect http(s) request to evo-http(s) protocol. - * See EMailRequest for further details about this. */ - if (uri_is_http) { - gchar *new_uri; - SoupURI *soup_uri; - gboolean image_exists; - - /* Check Evolution's cache */ - image_exists = html_editor_view_image_exists_in_cache (uri); - - settings = e_util_ref_settings ("org.gnome.evolution.mail"); - image_policy = g_settings_get_enum (settings, "image-loading-policy"); - g_object_unref (settings); - /* If the URI is not cached and we are not allowed to load it - * then redirect to invalid URI, so that webkit would display - * a native placeholder for it. */ - if (!image_exists && (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) { - return g_strdup ("about:blank"); - } - - new_uri = g_strconcat ("evo-", uri, NULL); - soup_uri = soup_uri_new (new_uri); - g_free (new_uri); - - new_uri = soup_uri_to_string (soup_uri, FALSE); - - soup_uri_free (soup_uri); - - return new_uri; - } - - return g_strdup (uri); -} - -static void -html_editor_view_resource_requested (WebKitWebView *web_view, - WebKitWebFrame *frame, - WebKitWebResource *resource, - WebKitNetworkRequest *request, - WebKitNetworkResponse *response, - gpointer user_data) -{ - const gchar *original_uri; - - original_uri = webkit_network_request_get_uri (request); - - if (original_uri != NULL) { - gchar *redirected_uri; - - redirected_uri = html_editor_view_redirect_uri ( - E_HTML_EDITOR_VIEW (web_view), original_uri); - - webkit_network_request_set_uri (request, redirected_uri); - - g_free (redirected_uri); - } -} - -static void -e_html_editor_view_class_init (EHTMLEditorViewClass *class) -{ - GObjectClass *object_class; - GtkWidgetClass *widget_class; - - g_type_class_add_private (class, sizeof (EHTMLEditorViewPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->get_property = html_editor_view_get_property; - object_class->set_property = html_editor_view_set_property; - object_class->dispose = html_editor_view_dispose; - object_class->finalize = html_editor_view_finalize; - object_class->constructed = html_editor_view_constructed; - - widget_class = GTK_WIDGET_CLASS (class); - widget_class->button_press_event = html_editor_view_button_press_event; - widget_class->button_release_event = html_editor_view_button_release_event; - widget_class->key_press_event = html_editor_view_key_press_event; - - class->paste_clipboard_quoted = html_editor_view_paste_clipboard_quoted; - - /** - * EHTMLEditorView:can-copy - * - * Determines whether it's possible to copy to clipboard. The action - * is usually disabled when there is no selection to copy. - */ - g_object_class_install_property ( - object_class, - PROP_CAN_COPY, - g_param_spec_boolean ( - "can-copy", - "Can Copy", - NULL, - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:can-cut - * - * Determines whether it's possible to cut to clipboard. The action - * is usually disabled when there is no selection to cut. - */ - g_object_class_install_property ( - object_class, - PROP_CAN_CUT, - g_param_spec_boolean ( - "can-cut", - "Can Cut", - NULL, - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:can-paste - * - * Determines whether it's possible to paste from clipboard. The action - * is usually disabled when there is no valid content in clipboard to - * paste. - */ - g_object_class_install_property ( - object_class, - PROP_CAN_PASTE, - g_param_spec_boolean ( - "can-paste", - "Can Paste", - NULL, - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:can-redo - * - * Determines whether it's possible to redo previous action. The action - * is usually disabled when there is no action to redo. - */ - g_object_class_install_property ( - object_class, - PROP_CAN_REDO, - g_param_spec_boolean ( - "can-redo", - "Can Redo", - NULL, - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:can-undo - * - * Determines whether it's possible to undo last action. The action - * is usually disabled when there is no previous action to undo. - */ - g_object_class_install_property ( - object_class, - PROP_CAN_UNDO, - g_param_spec_boolean ( - "can-undo", - "Can Undo", - NULL, - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:changed - * - * Determines whether document has been modified - */ - g_object_class_install_property ( - object_class, - PROP_CHANGED, - g_param_spec_boolean ( - "changed", - _("Changed property"), - _("Whether editor changed"), - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:html-mode - * - * Determines whether HTML or plain text mode is enabled. - **/ - g_object_class_install_property ( - object_class, - PROP_HTML_MODE, - g_param_spec_boolean ( - "html-mode", - "HTML Mode", - "Edit HTML or plain text", - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView::inline-spelling - * - * Determines whether automatic spellchecking is enabled. - */ - g_object_class_install_property ( - object_class, - PROP_INLINE_SPELLING, - g_param_spec_boolean ( - "inline-spelling", - "Inline Spelling", - "Check your spelling as you type", - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:magic-links - * - * Determines whether automatic conversion of text links into - * HTML links is enabled. - */ - g_object_class_install_property ( - object_class, - PROP_MAGIC_LINKS, - g_param_spec_boolean ( - "magic-links", - "Magic Links", - "Make URIs clickable as you type", - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:magic-smileys - * - * Determines whether automatic conversion of text smileys into - * images or Unicode characters is enabled. - */ - g_object_class_install_property ( - object_class, - PROP_MAGIC_SMILEYS, - g_param_spec_boolean ( - "magic-smileys", - "Magic Smileys", - "Convert emoticons to images or Unicode characters as you type", - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:unicode-smileys - * - * Determines whether Unicode characters should be used for smileys. - */ - g_object_class_install_property ( - object_class, - PROP_UNICODE_SMILEYS, - g_param_spec_boolean ( - "unicode-smileys", - "Unicode Smileys", - "Use Unicode characters for smileys", - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:spell-checker: - * - * The #ESpellChecker used for spell checking. - **/ - g_object_class_install_property ( - object_class, - PROP_SPELL_CHECKER, - g_param_spec_object ( - "spell-checker", - "Spell Checker", - "The spell checker", - E_TYPE_SPELL_CHECKER, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - /** - * EHTMLEditorView:popup-event - * - * Emitted whenever a context menu is requested. - */ - signals[POPUP_EVENT] = g_signal_new ( - "popup-event", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EHTMLEditorViewClass, popup_event), - g_signal_accumulator_true_handled, NULL, - e_marshal_BOOLEAN__BOXED, - G_TYPE_BOOLEAN, 1, - GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); - - /** - * EHTMLEditorView:paste-primary-clipboad - * - * Emitted when user presses middle button on EHTMLEditorView - */ - signals[PASTE_PRIMARY_CLIPBOARD] = g_signal_new ( - "paste-primary-clipboard", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EHTMLEditorViewClass, paste_primary_clipboard), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - /** - * EHTMLEditorView:is-ready - * - * Emitted when the view is ready. - */ - signals[IS_READY] = g_signal_new ( - "is-ready", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EHTMLEditorViewClass, is_ready), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -insert_quote_symbols (WebKitDOMDocument *document, - WebKitDOMHTMLElement *element, - gint quote_level) -{ - gchar *quotation; - WebKitDOMElement *quote_element; - - if (!WEBKIT_DOM_IS_HTML_ELEMENT (element)) - return; - - quotation = get_quotation_for_level (quote_level); - - quote_element = webkit_dom_document_create_element (document, "span", NULL); - element_add_class (quote_element, "-x-evo-quoted"); - - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (quote_element), quotation, NULL); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (quote_element), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)), - NULL); - - g_free (quotation); -} - -static void -quote_node (WebKitDOMDocument *document, - WebKitDOMNode *node, - gint quote_level) -{ - WebKitDOMNode *next_sibling; - WebKitDOMNode *parent; - - /* Don't quote when we are not in citation */ - if (quote_level == 0) - return; - - if (WEBKIT_DOM_IS_COMMENT (node)) - return; - - if (WEBKIT_DOM_IS_HTML_ELEMENT (node)) { - insert_quote_symbols ( - document, WEBKIT_DOM_HTML_ELEMENT (node), quote_level); - return; - } - - next_sibling = webkit_dom_node_get_next_sibling (node); - - /* Skip the BR between first blockquote and pre */ - if (quote_level == 1 && next_sibling && WEBKIT_DOM_IS_HTML_PRE_ELEMENT (next_sibling)) - return; - - parent = webkit_dom_node_get_parent_node (node), - - insert_quote_symbols ( - document, WEBKIT_DOM_HTML_ELEMENT (parent), quote_level); -} - -static void -insert_quote_symbols_before_node (WebKitDOMDocument *document, - WebKitDOMNode *node, - gint quote_level, - gboolean is_html_node) -{ - gboolean skip, wrap_br; - gchar *quotation; - WebKitDOMElement *element; - - quotation = get_quotation_for_level (quote_level); - element = webkit_dom_document_create_element (document, "SPAN", NULL); - element_add_class (element, "-x-evo-quoted"); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element), quotation, NULL); - - /* Don't insert temporary BR before BR that is used for wrapping */ - skip = WEBKIT_DOM_IS_HTMLBR_ELEMENT (node); - wrap_br = element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br"); - skip = skip && wrap_br; - - if (is_html_node && !skip) { - WebKitDOMElement *new_br; - - new_br = webkit_dom_document_create_element (document, "br", NULL); - element_add_class (new_br, "-x-evo-temp-br"); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (new_br), - node, - NULL); - } - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - node, - NULL); - - if (is_html_node && !wrap_br) - remove_node (node); - - g_free (quotation); -} - -static gboolean -check_if_suppress_next_node (WebKitDOMNode *node) -{ - if (!node) - return FALSE; - - if (node && WEBKIT_DOM_IS_ELEMENT (node)) - if (e_html_editor_node_is_selection_position_node (node)) - if (!webkit_dom_node_get_previous_sibling (node)) - return FALSE; - - return TRUE; -} - -static void -quote_br_node (WebKitDOMNode *node, - gint quote_level) -{ - gchar *quotation, *content; - - quotation = get_quotation_for_level (quote_level); - - content = g_strconcat ( - "<span class=\"-x-evo-quoted\">", - quotation, - "</span><br class=\"-x-evo-temp-br\">", - NULL); - - webkit_dom_html_element_set_outer_html ( - WEBKIT_DOM_HTML_ELEMENT (node), - content, - NULL); - - g_free (content); - g_free (quotation); -} - -static void -quote_plain_text_recursive (WebKitDOMDocument *document, - WebKitDOMNode *block, - WebKitDOMNode *start_node, - gint quote_level) -{ - gboolean skip_node = FALSE; - gboolean move_next = FALSE; - gboolean suppress_next = FALSE; - gboolean is_html_node = FALSE; - gboolean next = FALSE; - WebKitDOMNode *node; - WebKitDOMNode *next_sibling, *prev_sibling; - - node = webkit_dom_node_get_first_child (block); - - while (node) { - gchar *text_content; - - skip_node = FALSE; - move_next = FALSE; - is_html_node = FALSE; - - if (WEBKIT_DOM_IS_COMMENT (node) || - WEBKIT_DOM_IS_HTML_META_ELEMENT (node) || - WEBKIT_DOM_IS_HTML_STYLE_ELEMENT (node) || - WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node)) { - - move_next = TRUE; - goto next_node; - } - - prev_sibling = webkit_dom_node_get_previous_sibling (node); - next_sibling = webkit_dom_node_get_next_sibling (node); - - if (WEBKIT_DOM_IS_TEXT (node)) { - /* Start quoting after we are in blockquote */ - if (quote_level > 0 && !suppress_next) { - /* When quoting text node, we are wrappering it and - * afterwards replacing it with that wrapper, thus asking - * for next_sibling after quoting will return NULL bacause - * that node don't exist anymore */ - quote_node (document, node, quote_level); - node = next_sibling; - skip_node = TRUE; - } - - goto next_node; - } - - if (!(WEBKIT_DOM_IS_ELEMENT (node) || WEBKIT_DOM_IS_HTML_ELEMENT (node))) - goto next_node; - - if (e_html_editor_node_is_selection_position_node (node)) { - /* If there is collapsed selection in the beginning of line - * we cannot suppress first text that is after the end of - * selection */ - suppress_next = check_if_suppress_next_node (prev_sibling); - if (suppress_next) - next = FALSE; - move_next = TRUE; - goto next_node; - } - - if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) && - webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node)) != 0) - goto with_children; - - /* Even in plain text mode we can have some basic html element - * like anchor and others. When Forwaring e-mail as Quoted EMFormat - * generates header that contatains <b> tags (bold font). - * We have to treat these elements separately to avoid - * modifications of theirs inner texts */ - is_html_node = - WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) || - element_has_tag (WEBKIT_DOM_ELEMENT (node), "b") || - element_has_tag (WEBKIT_DOM_ELEMENT (node), "i") || - element_has_tag (WEBKIT_DOM_ELEMENT (node), "u") || - element_has_class (WEBKIT_DOM_ELEMENT (node), "Apple-tab-span"); - - if (is_html_node) { - gboolean wrap_br; - - wrap_br = - prev_sibling && - WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling) && - element_has_class ( - WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-wrap-br"); - - if (!prev_sibling || wrap_br) { - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - if (!prev_sibling && next_sibling && WEBKIT_DOM_IS_TEXT (next_sibling)) - suppress_next = TRUE; - } - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling) && !wrap_br) - insert_quote_symbols_before_node ( - document, prev_sibling, quote_level, TRUE); - - move_next = TRUE; - goto next_node; - } - - /* If element doesn't have children, we can quote it */ - if (is_citation_node (node)) { - /* Citation with just text inside */ - quote_node (document, node, quote_level + 1); - /* Set citation as quoted */ - element_add_class ( - WEBKIT_DOM_ELEMENT (node), - "-x-evo-plaintext-quoted"); - - move_next = TRUE; - goto next_node; - } - - if (!WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) { - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) { - move_next = TRUE; - goto next_node; - } - goto not_br; - } else if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-first-br") || - element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-last-br")) { - quote_br_node (node, quote_level); - node = next_sibling; - skip_node = TRUE; - goto next_node; - } - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) { - quote_br_node (prev_sibling, quote_level); - node = next_sibling; - skip_node = TRUE; - goto next_node; - } - - if (!prev_sibling && !next_sibling) { - WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); - - if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) || - WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent) || - (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && - !is_citation_node (parent))) { - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - - goto next_node; - } - } - - if (is_citation_node (prev_sibling)) { - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - goto next_node; - } - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (node) && - !next_sibling && WEBKIT_DOM_IS_ELEMENT (prev_sibling) && - e_html_editor_node_is_selection_position_node (prev_sibling)) { - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - goto next_node; - } - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) { - move_next = TRUE; - goto next_node; - } - - not_br: - text_content = webkit_dom_node_get_text_content (node); - if (text_content && !*text_content) { - g_free (text_content); - move_next = TRUE; - goto next_node; - } - g_free (text_content); - - quote_node (document, node, quote_level); - - move_next = TRUE; - goto next_node; - - with_children: - if (is_citation_node (node)) { - /* Go deeper and increase level */ - quote_plain_text_recursive ( - document, node, start_node, quote_level + 1); - /* set citation as quoted */ - element_add_class ( - WEBKIT_DOM_ELEMENT (node), - "-x-evo-plaintext-quoted"); - move_next = TRUE; - } else { - quote_plain_text_recursive ( - document, node, start_node, quote_level); - move_next = TRUE; - } - next_node: - if (next) { - suppress_next = FALSE; - next = FALSE; - } - - if (suppress_next) - next = TRUE; - - if (!skip_node) { - /* Move to next node */ - if (!move_next && webkit_dom_node_has_child_nodes (node)) { - node = webkit_dom_node_get_first_child (node); - } else if (webkit_dom_node_get_next_sibling (node)) { - node = webkit_dom_node_get_next_sibling (node); - } else { - return; - } - } - } -} - -WebKitDOMElement * -e_html_editor_view_quote_plain_text_element (EHTMLEditorView *view, - WebKitDOMElement *element) -{ - WebKitDOMDocument *document; - WebKitDOMNode *element_clone; - WebKitDOMNodeList *list; - gint ii, length, level; - - document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); - - element_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), TRUE); - level = get_citation_level (WEBKIT_DOM_NODE (element), TRUE); - - /* Remove old quote characters if the exists */ - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (element_clone), "span.-x-evo-quoted", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - webkit_dom_node_normalize (element_clone); - quote_plain_text_recursive ( - document, element_clone, element_clone, level); - - /* Set citation as quoted */ - if (is_citation_node (element_clone)) - element_add_class ( - WEBKIT_DOM_ELEMENT (element_clone), - "-x-evo-plaintext-quoted"); - - /* Replace old element with one, that is quoted */ - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - element_clone, - WEBKIT_DOM_NODE (element), - NULL); - - return WEBKIT_DOM_ELEMENT (element_clone); -} - -/** - * e_html_editor_view_quote_plain_text: - * @view: an #EHTMLEditorView - * - * Quote text inside citation blockquotes in plain text mode. - * - * As this function is cloning and replacing all citation blockquotes keep on - * mind that any pointers to nodes inside these blockquotes will be invalidated. - */ -WebKitDOMElement * -e_html_editor_view_quote_plain_text (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - WebKitDOMNode *body_clone; - WebKitDOMNamedNodeMap *attributes; - WebKitDOMNodeList *list; - WebKitDOMElement *element; - gint ii, length; - gulong attributes_length; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - /* Check if the document is already quoted */ - element = webkit_dom_document_query_selector ( - document, ".-x-evo-plaintext-quoted", NULL); - if (element) - return NULL; - - body = webkit_dom_document_get_body (document); - body_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE); - - /* Clean unwanted spaces before and after blockquotes */ - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (body_clone), "blockquote[type|=cite]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *blockquote = webkit_dom_node_list_item (list, ii); - WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (blockquote); - WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (blockquote); - - if (prev_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) - remove_node (prev_sibling); - - if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) - remove_node (next_sibling); - - if (webkit_dom_node_has_child_nodes (blockquote)) { - WebKitDOMNode *child = webkit_dom_node_get_first_child (blockquote); - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) - remove_node (child); - } - g_object_unref (blockquote); - } - g_object_unref (list); - - webkit_dom_node_normalize (body_clone); - quote_plain_text_recursive (document, body_clone, body_clone, 0); - - /* Copy attributes */ - attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); - attributes_length = webkit_dom_named_node_map_get_length (attributes); - for (ii = 0; ii < attributes_length; ii++) { - gchar *name, *value; - WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); - - name = webkit_dom_node_get_local_name (node); - value = webkit_dom_node_get_node_value (node); - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body_clone), name, value, NULL); - - g_object_unref (node); - g_free (name); - g_free (value); - } - g_object_unref (attributes); - - /* Replace old BODY with one, that is quoted */ - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)), - body_clone, - WEBKIT_DOM_NODE (body), - NULL); - - return WEBKIT_DOM_ELEMENT (body_clone); -} - -/** - * e_html_editor_view_dequote_plain_text: - * @view: an #EHTMLEditorView - * - * Dequote already quoted plain text in editor. - * Editor have to be quoted with e_html_editor_view_quote_plain_text otherwise - * it's not working. - */ -void -e_html_editor_view_dequote_plain_text (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMNodeList *paragraphs; - gint length, ii; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - paragraphs = webkit_dom_document_query_selector_all ( - document, "blockquote.-x-evo-plaintext-quoted", NULL); - length = webkit_dom_node_list_get_length (paragraphs); - for (ii = 0; ii < length; ii++) { - WebKitDOMElement *element; - - element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs, ii)); - - if (is_citation_node (WEBKIT_DOM_NODE (element))) { - element_remove_class (element, "-x-evo-plaintext-quoted"); - remove_quoting_from_element (element); - } - g_object_unref (element); - } - g_object_unref (paragraphs); -} - -static gboolean -create_anchor_for_link (const GMatchInfo *info, - GString *res, - gpointer data) -{ - gboolean link_surrounded, with_nbsp = FALSE; - gint offset = 0, truncate_from_end = 0; - gint match_start, match_end; - gchar *match_with_nbsp, *match_without_nbsp; - const gchar *end_of_match = NULL; - const gchar *match, *match_extra_characters; - - match_with_nbsp = g_match_info_fetch (info, 1); - /* E-mail addresses will be here. */ - match_without_nbsp = g_match_info_fetch (info, 0); - - if (!match_with_nbsp || (strstr (match_with_nbsp, " ") && !g_str_has_prefix (match_with_nbsp, " "))) { - match = match_without_nbsp; - match_extra_characters = match_with_nbsp; - g_match_info_fetch_pos (info, 0, &match_start, &match_end); - with_nbsp = TRUE; - } else { - match = match_with_nbsp; - match_extra_characters = match_without_nbsp; - g_match_info_fetch_pos (info, 1, &match_start, &match_end); - } - - if (g_str_has_prefix (match, " ")) - offset += 6; - - end_of_match = match + match_end - match_start - 1; - /* Taken from camel-url-scanner.c */ - /* URLs are extremely unlikely to end with any punctuation, so - * strip any trailing punctuation off from link and put it after - * the link. Do the same for any closing double-quotes as well. */ - while (end_of_match && end_of_match != match && strchr (URL_INVALID_TRAILING_CHARS, *end_of_match)) { - truncate_from_end++; - end_of_match--; - } - end_of_match++; - - link_surrounded = - g_str_has_suffix (res->str, "<"); - - if (link_surrounded) { - if (end_of_match && *end_of_match && strlen (match) > strlen (end_of_match) + 3) - link_surrounded = link_surrounded && g_str_has_prefix (end_of_match - 3, ">"); - else - link_surrounded = link_surrounded && g_str_has_suffix (match, ">"); - - if (link_surrounded) { - /* ";" is already counted by code above */ - truncate_from_end += 3; - end_of_match -= 3; - } - } - - g_string_append (res, "<a href=\""); - if (strstr (match, "@") && !strstr (match, "://")) - g_string_append (res, "mailto:"); - g_string_append (res, match + offset); - if (truncate_from_end > 0) - g_string_truncate (res, res->len - truncate_from_end); - - g_string_append (res, "\">"); - g_string_append (res, match + offset); - if (truncate_from_end > 0) - g_string_truncate (res, res->len - truncate_from_end); - - g_string_append (res, "</a>"); - - if (truncate_from_end > 0) - g_string_append (res, end_of_match); - - if (!with_nbsp && match_extra_characters) - g_string_append (res, match_extra_characters + (match_end - match_start)); - - g_free (match_with_nbsp); - g_free (match_without_nbsp); - - return FALSE; -} - -static gboolean -replace_to_nbsp (const GMatchInfo *info, - GString *res) -{ - gchar *match; - gint ii = 0; - - match = g_match_info_fetch (info, 0); - - while (match[ii] != '\0') { - if (match[ii] == ' ') { - /* Alone spaces or spaces before/after tabulator. */ - g_string_append (res, " "); - } else if (match[ii] == '\t') { - /* Replace tabs with their WebKit HTML representation. */ - g_string_append (res, "<span class=\"Apple-tab-span\" style=\"white-space:pre\">\t</span>"); - } - - ii++; - } - - g_free (match); - - return FALSE; -} - -static gboolean -surround_links_with_anchor (const gchar *text) -{ - return (strstr (text, "http") || strstr (text, "ftp") || - strstr (text, "www") || strstr (text, "@")); -} - -static void -append_new_block (WebKitDOMElement *parent, - WebKitDOMElement **block) -{ - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (parent), - WEBKIT_DOM_NODE (*block), - NULL); - - *block = NULL; -} - -static WebKitDOMElement * -create_and_append_new_block (EHTMLEditorSelection *selection, - WebKitDOMDocument *document, - WebKitDOMElement *parent, - WebKitDOMElement *block_template, - const gchar *content) -{ - WebKitDOMElement *block; - - block = WEBKIT_DOM_ELEMENT (webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (block_template), FALSE)); - - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (block), - content, - NULL); - - append_new_block (parent, &block); - - return block; -} - -static void -append_citation_mark (WebKitDOMDocument *document, - WebKitDOMElement *parent, - const gchar *citation_mark_text) -{ - WebKitDOMText *text; - - text = webkit_dom_document_create_text_node (document, citation_mark_text); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (parent), - WEBKIT_DOM_NODE (text), - NULL); -} - -static void -replace_selection_markers (gchar **text) -{ - if (!text) - return; - - if (strstr (*text, "##SELECTION_START##")) { - GString *tmp; - - tmp = e_str_replace_string ( - *text, - "##SELECTION_START##", - "<span id=\"-x-evo-selection-start-marker\"></span>"); - - g_free (*text); - *text = g_string_free (tmp, FALSE); - } - - if (strstr (*text, "##SELECTION_END##")) { - GString *tmp; - - tmp = e_str_replace_string ( - *text, - "##SELECTION_END##", - "<span id=\"-x-evo-selection-end-marker\"></span>"); - - g_free (*text); - *text = g_string_free (tmp, FALSE); - } -} - -static GString * -remove_new_lines_around_citations (const gchar *input) -{ - GString *str = NULL; - const gchar *p, *next; - - str = g_string_new (""); - - /* Remove the new lines around citations: - * Replace <br><br>##CITATION_START## with <br>##CITATION_START## - * Replace ##CITATION_START##<br><br> with ##CITATION_START##<br> - * Replace <br>##CITATION_END## with ##CITATION_END## */ - p = input; - while (next = strstr (p, "##CITATION_"), next) { - gchar citation_type = 0; - - if (p < next) - g_string_append_len (str, p, next - p); - - if (next + 11) - citation_type = next[11]; - /* ##CITATION_START## */ - if (citation_type == 'S') { - if (g_str_has_suffix (str->str, "<br><br>") || - g_str_has_suffix (str->str, "<br>")) - g_string_truncate (str, str->len - 4); - - if (g_str_has_prefix (next + 11, "START##<br><br>")) { - g_string_append (str, "##CITATION_START##<br>"); - p = next + 26; - continue; - } - } else if (citation_type == 'E') { - if (g_str_has_suffix (str->str, "<br>")) - g_string_truncate (str, str->len - 4); - } - - g_string_append (str, "##CITATION_"); - - p = next + 11; - } - - g_string_append (str, p); - - return str; -} - -static GString * -replace_citation_marks_to_citations (const gchar *input) -{ - GString *str = NULL; - const gchar *p, *next; - - str = g_string_new (""); - - /* Replaces text markers with actual HTML blockquotes */ - p = input; - while (next = strstr (p, "##CITATION_"), next) { - gchar citation_type = 0; - - if (p < next) - g_string_append_len (str, p, next - p); - - if (next + 11) - citation_type = next[11]; - /* ##CITATION_START## */ - if (citation_type == 'S') { - g_string_append (str, "<blockquote type=\"cite\">"); - p = next + 18; - } else if (citation_type == 'E') { - g_string_append (str, "</blockquote>"); - p = next + 16; - } else - p = next + 11; - } - - g_string_append (str, p); - - return str; -} - -/* This parses the HTML code (that contains just text, and BR elements) - * into blocks. - * HTML code in that format we can get by taking innerText from some element, - * setting it to another one and finally getting innerHTML from it */ -static void -parse_html_into_blocks (EHTMLEditorView *view, - WebKitDOMDocument *document, - WebKitDOMElement *parent, - WebKitDOMElement *passed_block_template, - const gchar *input) -{ - EHTMLEditorSelection *selection; - gboolean has_citation = FALSE, processing_last = FALSE; - const gchar *prev_br, *next_br; - GRegex *regex_nbsp = NULL, *regex_link = NULL, *regex_email = NULL; - GString *html = NULL; - WebKitDOMElement *block_template = passed_block_template; - - selection = e_html_editor_view_get_selection (view); - - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (parent), "", NULL); - - if (!block_template) { - gboolean use_paragraphs; - GSettings *settings; - - settings = e_util_ref_settings ("org.gnome.evolution.mail"); - - use_paragraphs = g_settings_get_boolean ( - settings, "composer-wrap-quoted-text-in-replies"); - - if (use_paragraphs) - block_template = e_html_editor_selection_get_paragraph_element ( - selection, document, -1, 0); - else - block_template = webkit_dom_document_create_element (document, "pre", NULL); - - g_object_unref (settings); - } - - /* Replace the tabulators with SPAN elements that corresponds to them. - * If not inserting the content into the PRE element also replace single - * spaces on the beginning of line, 2+ spaces and with non breaking - * spaces. */ - if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block_template)) - regex_nbsp = g_regex_new ("\x9", 0, 0, NULL); - else - regex_nbsp = g_regex_new ("^\\s{1}|\\s{2,}|\x9|\\s$", 0, 0, NULL); - - html = remove_new_lines_around_citations (input); - - prev_br = html->str; - next_br = strstr (prev_br, "<br>"); - while (next_br) { - const gchar *citation_start = NULL, *citation_end = NULL; - const gchar *rest = NULL, *with_br = NULL; - gchar *to_process = NULL, *to_insert = NULL; - guint to_insert_start = 0, to_insert_end = 0; - - if ((to_process = g_utf8_substring (prev_br, 0, g_utf8_pointer_to_offset (prev_br, next_br))) && !*to_process && !processing_last) { - g_free (to_process); - to_process = g_strdup (next_br); - processing_last = TRUE; - } - to_insert_end = g_utf8_strlen (to_process, -1); - - if ((with_br = strstr (to_process, "<br>"))) { - if (with_br == to_process) - to_insert_start += 4; - } - - if ((citation_start = strstr (to_process, "##CITATION_START"))) { - if (with_br && citation_start == with_br + 4) - to_insert_start += 18; /* + ## */ - else - to_insert_end -= 18; /* + ## */ - has_citation = TRUE; - } - if ((citation_end = strstr (to_process, "##CITATION_END"))) - to_insert_end -= 16; /* + ## */ - - /* First BR */ - if (with_br && prev_br == html->str) - create_and_append_new_block ( - selection, - document, - parent, - block_template, - "<br id=\"-x-evo-first-br\">"); - - if (with_br && citation_start && citation_start == with_br + 4) { - create_and_append_new_block ( - selection, document, parent, block_template, "<br>"); - - append_citation_mark (document, parent, "##CITATION_START##"); - } - - if ((to_insert = g_utf8_substring (to_process, to_insert_start, to_insert_end)) && *to_insert) { - gboolean empty = FALSE; - gchar *truncated = g_strdup (to_insert); - gchar *rest_to_insert; - - empty = !*truncated && strlen (to_insert) > 0; - - rest_to_insert = g_regex_replace_eval ( - regex_nbsp, - empty ? rest : truncated, - -1, - 0, - 0, - (GRegexEvalCallback) replace_to_nbsp, - NULL, - NULL); - g_free (truncated); - - replace_selection_markers (&rest_to_insert); - - if (surround_links_with_anchor (rest_to_insert)) { - gboolean is_email_address = - strstr (rest_to_insert, "@") && - !strstr (rest_to_insert, "://"); - - if (is_email_address && !regex_email) - regex_email = g_regex_new (E_MAIL_PATTERN, 0, 0, NULL); - if (!is_email_address && !regex_link) - regex_link = g_regex_new (URL_PATTERN, 0, 0, NULL); - - truncated = g_regex_replace_eval ( - is_email_address ? regex_email : regex_link, - rest_to_insert, - -1, - 0, - G_REGEX_MATCH_NOTEMPTY, - create_anchor_for_link, - NULL, - NULL); - - g_free (rest_to_insert); - rest_to_insert = truncated; - } - - create_and_append_new_block ( - selection, document, parent, block_template, rest_to_insert); - - g_free (rest_to_insert); - } else if (to_insert && !citation_start) - create_and_append_new_block ( - selection, document, parent, block_template, "<br>"); - - g_free (to_insert); - - if (with_br && citation_start && citation_start != with_br + 4) - append_citation_mark (document, parent, "##CITATION_START##"); - - if (citation_end) - append_citation_mark (document, parent, "##CITATION_END##"); - - prev_br = next_br; - next_br = strstr (prev_br + 4, "<br>"); - if (!next_br && !processing_last) { - if (g_utf8_strlen (prev_br, -1) > 4) - next_br = prev_br; - else { - WebKitDOMNode *child; - - child = webkit_dom_node_get_last_child ( - WEBKIT_DOM_NODE (parent)); - if (child) { - child = webkit_dom_node_get_first_child (child); - if (child && WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) { - /* If the processed HTML contained just - * the BR don't overwrite its id. */ - if (!element_has_id (WEBKIT_DOM_ELEMENT (child), "-x-evo-first-br")) - webkit_dom_element_set_id ( - WEBKIT_DOM_ELEMENT (child), - "-x-evo-last-br"); - } else if (!view->priv->is_editting_message) - create_and_append_new_block ( - selection, document, parent, block_template, "<br>"); - } else - create_and_append_new_block ( - selection, document, parent, block_template, "<br>"); - g_free (to_process); - break; - } - processing_last = TRUE; - } - g_free (to_process); - } - - if (has_citation) { - gchar *inner_html; - GString *parsed; - - /* Replace text markers with actual HTML blockquotes */ - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (parent)); - parsed = replace_citation_marks_to_citations (inner_html); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (parent), parsed->str, NULL); - - g_free (inner_html); - g_string_free (parsed, TRUE); - } - - g_string_free (html, TRUE); - - if (regex_email != NULL) - g_regex_unref (regex_email); - if (regex_link != NULL) - g_regex_unref (regex_link); - g_regex_unref (regex_nbsp); -} - -void -e_html_editor_view_insert_quoted_text (EHTMLEditorView *view, - const gchar *text) -{ - EHTMLEditorSelection *selection; - EHTMLEditorViewHistoryEvent *ev = NULL; - gchar *inner_html; - WebKitDOMDocument *document; - WebKitDOMElement *blockquote, *element, *selection_start; - WebKitDOMNode *node; - - if (!text || !*text) - return; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - /* This is a trick to escape any HTML characters (like <, > or &). - * <textarea> automatically replaces all these unsafe characters - * by <, > etc. */ - element = webkit_dom_document_create_element (document, "textarea", NULL); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element), text, NULL); - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element)); - - e_html_editor_selection_save (selection); - - if (!e_html_editor_view_is_undo_redo_in_progress (view)) { - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_PASTE_QUOTED; - - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->before.start.x, - &ev->before.start.y, - &ev->before.end.x, - &ev->before.end.y); - ev->data.string.from = NULL; - ev->data.string.to = g_strdup (text); - } - - blockquote = webkit_dom_document_create_element (document, "blockquote", NULL); - webkit_dom_element_set_attribute (blockquote, "type", "cite", NULL); - - selection_start = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start)); - /* Check if block is empty. If so, replace it otherwise insert the quoted - * content after current block. */ - if (!node || WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) { - node = webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (selection_start)); - node = webkit_dom_node_get_next_sibling (node); - if (!node || WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) { - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start))), - WEBKIT_DOM_NODE (blockquote), - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start)), - NULL); - } - } else { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)), - WEBKIT_DOM_NODE (blockquote), - webkit_dom_node_get_next_sibling ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start))), - NULL); - } - - parse_html_into_blocks (view, document, blockquote, NULL, inner_html); - - if (e_html_editor_view_get_html_mode (view)) { - node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (blockquote)); - } else { - gint word_wrap_length; - - element_add_class (blockquote, "-x-evo-plaintext-quoted"); - word_wrap_length = e_html_editor_selection_get_word_wrap_length (selection); - node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (blockquote)); - while (node) { - WebKitDOMNode *next_sibling; - - node = WEBKIT_DOM_NODE (e_html_editor_selection_wrap_paragraph_length ( - selection, WEBKIT_DOM_ELEMENT (node), word_wrap_length - 2)); - - webkit_dom_node_normalize (node); - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, WEBKIT_DOM_ELEMENT (node), 1); - - next_sibling = webkit_dom_node_get_next_sibling (node); - if (!next_sibling) - break; - - node = next_sibling; - } - } - - add_selection_markers_into_element_end ( - document, WEBKIT_DOM_ELEMENT (node), NULL, NULL); - - e_html_editor_selection_restore (selection); - - if (ev) { - e_html_editor_selection_get_selection_coordinates ( - selection, - &ev->after.start.x, - &ev->after.start.y, - &ev->after.end.x, - &ev->after.end.y); - e_html_editor_view_insert_new_history_event (view, ev); - } - - e_html_editor_view_force_spell_check_in_viewport (view); - - e_html_editor_view_set_changed (view, TRUE); - - g_free (inner_html); -} - -static void -mark_citation (WebKitDOMElement *citation) -{ - webkit_dom_html_element_insert_adjacent_text ( - WEBKIT_DOM_HTML_ELEMENT (citation), - "beforebegin", - "##CITATION_START##", - NULL); - - webkit_dom_html_element_insert_adjacent_text ( - WEBKIT_DOM_HTML_ELEMENT (citation), - "afterend", - "##CITATION_END##", - NULL); - - element_add_class (citation, "marked"); -} - -static gint -create_text_markers_for_citations_in_element (WebKitDOMElement *element) -{ - gint count = 0; - WebKitDOMElement *citation; - - citation = webkit_dom_element_query_selector ( - element, "blockquote[type=cite]:not(.marked)", NULL); - - while (citation) { - mark_citation (citation); - count ++; - - citation = webkit_dom_element_query_selector ( - element, "blockquote[type=cite]:not(.marked)", NULL); - } - - return count; -} - -static void -create_text_markers_for_selection_in_element (WebKitDOMElement *element) -{ - WebKitDOMElement *selection_marker; - - selection_marker = webkit_dom_element_query_selector ( - element, "#-x-evo-selection-start-marker", NULL); - if (selection_marker) - webkit_dom_html_element_insert_adjacent_text ( - WEBKIT_DOM_HTML_ELEMENT (selection_marker), - "afterend", - "##SELECTION_START##", - NULL); - - selection_marker = webkit_dom_element_query_selector ( - element, "#-x-evo-selection-end-marker", NULL); - if (selection_marker) - webkit_dom_html_element_insert_adjacent_text ( - WEBKIT_DOM_HTML_ELEMENT (selection_marker), - "afterend", - "##SELECTION_END##", - NULL); -} - -static void -quote_plain_text_elements_after_wrapping_in_document (WebKitDOMDocument *document) -{ - gint length, ii; - WebKitDOMNodeList *list; - - /* Also quote the PRE elements as well. */ - list = webkit_dom_document_query_selector_all ( - document, "blockquote[type=cite] > div.-x-evo-paragraph, blockquote[type=cite] > pre", NULL); - - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - gint citation_level; - WebKitDOMNode *child; - - child = webkit_dom_node_list_item (list, ii); - citation_level = get_citation_level (child, TRUE); - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, WEBKIT_DOM_ELEMENT (child), citation_level); - g_object_unref (child); - } - g_object_unref (list); -} - -static void -clear_attributes (WebKitDOMDocument *document) -{ - gint length, ii; - WebKitDOMNamedNodeMap *attributes; - WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document); - WebKitDOMHTMLHeadElement *head = webkit_dom_document_get_head (document); - WebKitDOMElement *document_element = - webkit_dom_document_get_document_element (document); - - /* Remove all attributes from HTML element */ - attributes = webkit_dom_element_get_attributes (document_element); - length = webkit_dom_named_node_map_get_length (attributes); - for (ii = length - 1; ii >= 0; ii--) { - WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); - - webkit_dom_element_remove_attribute_node ( - document_element, WEBKIT_DOM_ATTR (node), NULL); - g_object_unref (node); - } - g_object_unref (attributes); - - /* Remove everything from HEAD element */ - while (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head))) - remove_node (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head))); - - /* Make the quote marks non-selectable. */ - disable_quote_marks_select (document); - - /* Remove non Evolution attributes from BODY element */ - attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); - length = webkit_dom_named_node_map_get_length (attributes); - for (ii = length - 1; ii >= 0; ii--) { - gchar *name; - WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); - - name = webkit_dom_node_get_local_name (node); - - if (!g_str_has_prefix (name, "data-") && (g_strcmp0 (name, "spellcheck") != 0)) - webkit_dom_element_remove_attribute_node ( - WEBKIT_DOM_ELEMENT (body), - WEBKIT_DOM_ATTR (node), - NULL); - - g_free (name); - g_object_unref (node); - } - g_object_unref (attributes); -} - -static void -body_compositionstart_event_cb (WebKitDOMElement *element, - WebKitDOMUIEvent *event, - EHTMLEditorView *view) -{ - view->priv->composition_in_progress = TRUE; - e_html_editor_view_remove_input_event_listener_from_body (view); -} - -static void -body_compositionend_event_cb (WebKitDOMElement *element, - WebKitDOMUIEvent *event, - EHTMLEditorView *view) -{ - view->priv->composition_in_progress = FALSE; - e_html_editor_view_register_input_event_listener_on_body (view); -} - -static void -register_html_events_handlers (EHTMLEditorView *view, - WebKitDOMHTMLElement *body) -{ - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (body), - "keydown", - G_CALLBACK (body_keydown_event_cb), - FALSE, - view); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (body), - "keypress", - G_CALLBACK (body_keypress_event_cb), - FALSE, - view); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (body), - "keyup", - G_CALLBACK (body_keyup_event_cb), - FALSE, - view); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (body), - "compositionstart", - G_CALLBACK (body_compositionstart_event_cb), - FALSE, - view); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (body), - "compositionend", - G_CALLBACK (body_compositionend_event_cb), - FALSE, - view); -} - -static gboolean -add_signature_delimiter (EHTMLEditorView *view) -{ - return !g_settings_get_boolean (view->priv->mail_settings, "composer-no-signature-delim"); -} - -static gboolean -use_top_signature (EHTMLEditorView *view) -{ - return g_settings_get_boolean (view->priv->mail_settings, "composer-top-signature"); -} - -static gboolean -start_typing_at_bottom (EHTMLEditorView *view) -{ - return g_settings_get_boolean (view->priv->mail_settings, "composer-reply-start-bottom"); -} - -static void -html_editor_convert_view_content (EHTMLEditorView *view, - const gchar *preferred_text) -{ - EHTMLEditorSelection *selection = e_html_editor_view_get_selection (view); - gboolean start_bottom, empty = FALSE; - gchar *inner_html; - gint ii, length; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMElement *paragraph, *content_wrapper, *top_signature; - WebKitDOMElement *cite_body, *signature, *wrapper; - WebKitDOMHTMLElement *body; - WebKitDOMNodeList *list; - WebKitDOMNode *node; - - start_bottom = start_typing_at_bottom (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - body = webkit_dom_document_get_body (document); - /* Wrapper that will represent the new body. */ - wrapper = webkit_dom_document_create_element (document, "div", NULL); - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-converted", "", NULL); - - cite_body = webkit_dom_document_query_selector ( - document, "span.-x-evo-cite-body", NULL); - - /* content_wrapper when the processed text will be placed. */ - content_wrapper = webkit_dom_document_create_element ( - document, cite_body ? "blockquote" : "div", NULL); - if (cite_body) { - webkit_dom_element_set_attribute (content_wrapper, "type", "cite", NULL); - webkit_dom_element_set_attribute (content_wrapper, "id", "-x-evo-main-cite", NULL); - } - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (content_wrapper), NULL); - - /* Remove all previously inserted paragraphs. */ - list = webkit_dom_document_query_selector_all ( - document, ".-x-evo-paragraph:not([data-headers])", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - /* Insert the paragraph where the caret will be. */ - paragraph = prepare_paragraph (selection, document, TRUE); - webkit_dom_element_set_id (paragraph, "-x-evo-input-start"); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (wrapper), - WEBKIT_DOM_NODE (paragraph), - start_bottom ? - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (content_wrapper)) : - WEBKIT_DOM_NODE (content_wrapper), - NULL); - - /* Insert signature (if presented) to the right position. */ - top_signature = webkit_dom_document_query_selector ( - document, ".-x-evo-top-signature", NULL); - signature = webkit_dom_document_query_selector ( - document, ".-x-evo-signature-content_wrapper", NULL); - if (signature) { - if (top_signature) { - WebKitDOMElement *spacer; - - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (wrapper), - WEBKIT_DOM_NODE (signature), - start_bottom ? - WEBKIT_DOM_NODE (content_wrapper) : - webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (paragraph)), - NULL); - /* Insert NL after the signature */ - spacer = prepare_paragraph (selection, document, FALSE); - element_add_class (spacer, "-x-evo-top-signature-spacer"); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (wrapper), - WEBKIT_DOM_NODE (spacer), - webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (signature)), - NULL); - } else { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (wrapper), - WEBKIT_DOM_NODE (signature), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE ( - start_bottom ? paragraph : content_wrapper)), - NULL); - } - } - - /* Move credits to the body */ - list = webkit_dom_document_query_selector_all ( - document, "span.-x-evo-to-body[data-credits]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - char *credits; - WebKitDOMElement *element; - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - element = e_html_editor_selection_get_paragraph_element (selection, document, -1, 0); - credits = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "data-credits"); - webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element), credits, NULL); - g_free (credits); - - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (wrapper), - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (content_wrapper), - NULL); - - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - /* Move headers to body */ - list = webkit_dom_document_query_selector_all ( - document, "div[data-headers]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (list, ii); - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (node), "data-headers"); - e_html_editor_selection_set_paragraph_style ( - selection, WEBKIT_DOM_ELEMENT (node), -1, 0, ""); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (wrapper), - node, - WEBKIT_DOM_NODE (content_wrapper), - NULL); - - g_object_unref (node); - } - g_object_unref (list); - - repair_gmail_blockquotes (document); - remove_thunderbird_signature (document); - create_text_markers_for_citations_in_element (WEBKIT_DOM_ELEMENT (body)); - - if (preferred_text && *preferred_text) - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (content_wrapper), preferred_text, NULL); - else { - gchar *inner_text; - - inner_text = webkit_dom_html_element_get_inner_text (body); - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (content_wrapper), inner_text, NULL); - - g_free (inner_text); - } - - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (content_wrapper)); - - /* Replace the old body with the new one. */ - node = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE); - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)), - node, - WEBKIT_DOM_NODE (body), - NULL); - body = WEBKIT_DOM_HTML_ELEMENT (node); - - /* Copy all to nodes to the new body. */ - while ((node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (wrapper)))) { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (node), - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), - NULL); - } - remove_node (WEBKIT_DOM_NODE (wrapper)); - - if (inner_html && !*inner_html) - empty = TRUE; - - length = webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (body)); - if (length <= 1) { - empty = TRUE; - if (length == 1) { - WebKitDOMNode *child; - - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - empty = child && WEBKIT_DOM_IS_HTMLBR_ELEMENT (child); - } - } - - if (preferred_text && *preferred_text) - empty = FALSE; - - if (!empty) - parse_html_into_blocks (view, document, content_wrapper, NULL, inner_html); - else - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (content_wrapper), - WEBKIT_DOM_NODE (prepare_paragraph (selection, document, FALSE)), - NULL); - - if (!cite_body) { - if (!empty) { - WebKitDOMNode *child; - - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (content_wrapper)))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (content_wrapper)), - child, - WEBKIT_DOM_NODE (content_wrapper), - NULL); - } - } - - remove_node (WEBKIT_DOM_NODE (content_wrapper)); - } - - /* If not editting a message, don't add any new block and just place - * the carret in the beginning of content. We want to have the same - * behaviour when editting message as new or we start replying on top. */ - if (!view->priv->is_editting_message || view->priv->is_message_from_edit_as_new || !start_bottom) { - WebKitDOMNode *child; - - remove_node (WEBKIT_DOM_NODE (paragraph)); - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - if (child) - add_selection_markers_into_element_start ( - document, WEBKIT_DOM_ELEMENT (child), NULL, NULL); - } - - if ((paragraph = webkit_dom_document_get_element_by_id (document, "-x-evo-last-br"))) - webkit_dom_element_remove_attribute (paragraph, "id"); - if ((paragraph = webkit_dom_document_get_element_by_id (document, "-x-evo-first-br"))) - webkit_dom_element_remove_attribute (paragraph, "id"); - - merge_siblings_if_necessary (document, NULL); - - if (!e_html_editor_view_get_html_mode (view)) { - e_html_editor_selection_wrap_paragraphs_in_document ( - selection, document); - - quote_plain_text_elements_after_wrapping_in_document (document); - } - - clear_attributes (document); - - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_in_viewport (view); - - /* Register on input event that is called when the content (body) is modified */ - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (body), - "input", - G_CALLBACK (body_input_event_cb), - FALSE, - view); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (dom_window), - "scroll", - G_CALLBACK (body_scroll_event_cb), - FALSE, - view); - - register_html_events_handlers (view, body); - set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), view->priv->html_mode); - - g_free (inner_html); -} - -static void -fix_structure_after_pasting_multiline_content (WebKitDOMNode *node) -{ - WebKitDOMNode *first_child, *parent; - - /* When pasting content that does not contain just the - * one line text WebKit inserts all the content after the - * first line into one element. So we have to take it out - * of this element and insert it after that element. */ - parent = webkit_dom_node_get_parent_node (node); - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) - return; - first_child = webkit_dom_node_get_first_child (parent); - while (first_child) { - WebKitDOMNode *next_child = - webkit_dom_node_get_next_sibling (first_child); - if (webkit_dom_node_has_child_nodes (first_child)) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - first_child, - parent, - NULL); - first_child = next_child; - } -} - -static void -html_editor_view_insert_converted_html_into_selection (EHTMLEditorView *view, - gboolean is_html, - const gchar *html) -{ - EHTMLEditorSelection *selection = e_html_editor_view_get_selection (view); - gboolean has_selection; - gchar *inner_html; - gint citation_level; - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker, *selection_end_marker, *element; - WebKitDOMNode *node; - WebKitDOMNode *current_block; - - e_html_editor_view_remove_input_event_listener_from_body (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - e_html_editor_selection_save (selection); - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - current_block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block)) - current_block = NULL; - - element = webkit_dom_document_create_element (document, "div", NULL); - if (is_html) { - gchar *inner_text; - - if (strstr (html, "\n")) { - GRegex *regex; - gchar *tmp; - - /* Strip new lines between tags to avoid unwanted line breaks. */ - regex = g_regex_new ("\\>[\\s]+\\<", 0, 0, NULL); - tmp = g_regex_replace ( - regex, html, -1, 0, "> <", 0, NULL); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element), tmp, NULL); - g_free (tmp); - g_regex_unref (regex); - } else { - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element), html, NULL); - } - - inner_text = webkit_dom_html_element_get_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (element)); - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (element), inner_text, NULL); - - g_free (inner_text); - } else - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (element), html, NULL); - - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element)); - parse_html_into_blocks (view, document, element, WEBKIT_DOM_ELEMENT (current_block), inner_html); - g_free (inner_html); - - has_selection = !e_html_editor_selection_is_collapsed (selection); - if (has_selection && !view->priv->undo_redo_in_progress) { - if (!view->priv->undo_redo_in_progress) { - WebKitDOMRange *range; - - range = html_editor_view_get_dom_range (view); - insert_delete_event (view, range); - g_object_unref (range); - } - - /* Remove the text that was meant to be replaced by the pasted text */ - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL); - - e_html_editor_selection_save (selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - current_block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block)) - current_block = NULL; - } - - citation_level = get_citation_level (WEBKIT_DOM_NODE (selection_end_marker), FALSE); - /* Pasting into the citation */ - if (citation_level > 0) { - gint length; - gint word_wrap_length = e_html_editor_selection_get_word_wrap_length (selection); - WebKitDOMElement *br; - WebKitDOMNode *first_paragraph, *last_paragraph; - WebKitDOMNode *child, *parent, *current_block; - - first_paragraph = webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (element)); - last_paragraph = webkit_dom_node_get_last_child ( - WEBKIT_DOM_NODE (element)); - - length = word_wrap_length - 2 * citation_level; - - /* Pasting text that was parsed just into one paragraph */ - if (webkit_dom_node_is_same_node (first_paragraph, last_paragraph)) { - WebKitDOMNode *child, *parent_block; - WebKitDOMNode *parent; - - parent_block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent_block)); - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent_block)); - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); - while ((child = webkit_dom_node_get_first_child (first_paragraph))) { - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) && - WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (child)) { - WebKitDOMNode *anchor_child; - - while ((anchor_child = webkit_dom_node_get_first_child (child))) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)), - anchor_child, - WEBKIT_DOM_NODE (selection_start_marker), - NULL); - remove_node (child); - } else - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)), - child, - WEBKIT_DOM_NODE (selection_start_marker), - NULL); - } - - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) { - gchar *text_content; - - text_content = webkit_dom_node_get_text_content (parent); - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (parent), - "href", - text_content, - NULL); - g_free (text_content); - } - - parent_block = WEBKIT_DOM_NODE ( - e_html_editor_selection_wrap_paragraph_length ( - selection, WEBKIT_DOM_ELEMENT (parent_block), length)); - webkit_dom_node_normalize (parent_block); - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, WEBKIT_DOM_ELEMENT (parent_block), citation_level); - - e_html_editor_selection_restore (selection); - - g_object_unref (element); - goto out; - } - - /* Pasting content parsed into the multiple paragraphs */ - parent = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent)); - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent)); - - /* Move the elements from the first paragraph before the selection start element */ - while ((child = webkit_dom_node_get_first_child (first_paragraph))) - webkit_dom_node_insert_before ( - parent, - child, - WEBKIT_DOM_NODE (selection_start_marker), - NULL); - - remove_node (first_paragraph); - - /* If the BR element is on the last position, remove it as we don't need it */ - child = webkit_dom_node_get_last_child (parent); - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) - remove_node (child); - - parent = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_end_marker)); - - child = webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (selection_end_marker)); - /* Move the elements that are in the same paragraph as the selection end - * on the end of pasted text, but avoid BR on the end of paragraph */ - while (child) { - WebKitDOMNode *next_child = - webkit_dom_node_get_next_sibling (child); - if (!(!next_child && WEBKIT_DOM_IS_HTMLBR_ELEMENT (child))) - webkit_dom_node_append_child (last_paragraph, child, NULL); - child = next_child; - } - - current_block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - - remove_selection_markers (document); - - /* Caret will be restored on the end of pasted text */ - webkit_dom_node_append_child ( - last_paragraph, - WEBKIT_DOM_NODE (create_selection_marker (document, TRUE)), - NULL); - - webkit_dom_node_append_child ( - last_paragraph, - WEBKIT_DOM_NODE (create_selection_marker (document, FALSE)), - NULL); - - /* Insert the paragraph with the end of the pasted text after - * the paragraph that contains the selection end */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - last_paragraph, - webkit_dom_node_get_next_sibling (parent), - NULL); - - /* Wrap, quote and move all paragraphs from pasted text into the body */ - while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) { - child = WEBKIT_DOM_NODE (e_html_editor_selection_wrap_paragraph_length ( - selection, WEBKIT_DOM_ELEMENT (child), length)); - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, WEBKIT_DOM_ELEMENT (child), citation_level); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (last_paragraph), - child, - last_paragraph, - NULL); - } - - webkit_dom_node_normalize (last_paragraph); - - last_paragraph = WEBKIT_DOM_NODE ( - e_html_editor_selection_wrap_paragraph_length ( - selection, WEBKIT_DOM_ELEMENT (last_paragraph), length)); - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, WEBKIT_DOM_ELEMENT (last_paragraph), citation_level); - - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent)); - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent)); - - current_block = WEBKIT_DOM_NODE (e_html_editor_selection_wrap_paragraph_length ( - selection, WEBKIT_DOM_ELEMENT (current_block), length)); - e_html_editor_view_quote_plain_text_element_after_wrapping ( - document, WEBKIT_DOM_ELEMENT (current_block), citation_level); - - if ((br = webkit_dom_document_get_element_by_id (document, "-x-evo-last-br"))) - webkit_dom_element_remove_attribute (br, "class"); - - if ((br = webkit_dom_document_get_element_by_id (document, "-x-evo-first-br"))) - webkit_dom_element_remove_attribute (br, "class"); - - e_html_editor_selection_restore (selection); - - g_object_unref (element); - goto out; - } - - remove_node (WEBKIT_DOM_NODE (selection_start_marker)); - remove_node (WEBKIT_DOM_NODE (selection_end_marker)); - - /* If the text to insert was converted just to one block, pass just its - * text to WebKit otherwise WebKit will insert unwanted block with - * extra new line. */ - if (!webkit_dom_node_get_next_sibling (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))); - else - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element)); - - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, inner_html); - - if (g_str_has_suffix (inner_html, " ")) - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, " "); - g_free (inner_html); - - g_object_unref (element); - e_html_editor_selection_save (selection); - - element = webkit_dom_document_query_selector ( - document, "* > br#-x-evo-first-br", NULL); - if (element) { - WebKitDOMNode *sibling; - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (element)); - - sibling = webkit_dom_node_get_previous_sibling (parent); - if (sibling) - remove_node (WEBKIT_DOM_NODE (parent)); - else - webkit_dom_element_remove_attribute (element, "class"); - } - - element = webkit_dom_document_query_selector ( - document, "* > br#-x-evo-last-br", NULL); - if (element) { - WebKitDOMNode *parent; - WebKitDOMNode *child; - - parent = webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (element)); - - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)); - if (node) { - node = webkit_dom_node_get_first_child (node); - if (node) { - inner_html = webkit_dom_node_get_text_content (node); - if (g_str_has_prefix (inner_html, UNICODE_NBSP)) - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL); - g_free (inner_html); - } - } - - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - if (has_selection) { - /* Everything after the selection end marker have to be in separate - * paragraph */ - child = webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (selection_end_marker)); - /* Move the elements that are in the same paragraph as the selection end - * on the end of pasted text, but avoid BR on the end of paragraph */ - while (child) { - WebKitDOMNode *next_child = - webkit_dom_node_get_next_sibling (child); - if (!(!next_child && WEBKIT_DOM_IS_HTMLBR_ELEMENT (child))) - webkit_dom_node_append_child (parent, child, NULL); - child = next_child; - } - - remove_node (WEBKIT_DOM_NODE (element)); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_end_marker))), - parent, - webkit_dom_node_get_next_sibling ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_end_marker))), - NULL); - node = parent; - } else { - node = webkit_dom_node_get_next_sibling (parent); - if (!node) { - fix_structure_after_pasting_multiline_content (parent); - if (!webkit_dom_node_get_first_child (parent)) - remove_node (parent); - } - } - - if (node) { - /* Restore caret on the end of pasted text */ - webkit_dom_node_insert_before ( - node, - WEBKIT_DOM_NODE (selection_end_marker), - webkit_dom_node_get_first_child (node), - NULL); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - webkit_dom_node_insert_before ( - node, - WEBKIT_DOM_NODE (selection_start_marker), - webkit_dom_node_get_first_child (node), - NULL); - } - - if (element) - webkit_dom_element_remove_attribute (element, "class"); - - if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)) && !has_selection) - remove_node (parent); - } else { - /* When pasting the content that was copied from the composer, WebKit - * restores the selection wrongly, thus is saved wrongly and we have - * to fix it */ - WebKitDOMNode *block, *parent, *clone1, *clone2; - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - selection_end_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - parent = webkit_dom_node_get_parent_node (block); - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (parent), "id"); - - /* Check if WebKit created wrong structure */ - clone1 = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (block), FALSE); - clone2 = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (parent), FALSE); - if (webkit_dom_node_is_equal_node (clone1, clone2) || - (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (clone1) && WEBKIT_DOM_IS_HTML_DIV_ELEMENT (clone2) && - !element_has_class (WEBKIT_DOM_ELEMENT (clone2), "-x-evo-indented"))) { - fix_structure_after_pasting_multiline_content (block); - if (g_strcmp0 (html, "\n") == 0) { - WebKitDOMElement *br; - - br = webkit_dom_document_create_element (document, "br", NULL); - webkit_dom_node_append_child ( - parent, WEBKIT_DOM_NODE (br), NULL); - - webkit_dom_node_insert_before ( - parent, - WEBKIT_DOM_NODE (selection_start_marker), - webkit_dom_node_get_last_child (parent), - NULL); - } else if (!webkit_dom_node_get_first_child (parent)) - remove_node (parent); - } - - g_object_unref (clone1); - g_object_unref (clone2); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (selection_start_marker)), - WEBKIT_DOM_NODE (selection_end_marker), - webkit_dom_node_get_next_sibling ( - WEBKIT_DOM_NODE (selection_start_marker)), - NULL); - } - - e_html_editor_selection_restore (selection); - out: - e_html_editor_view_force_spell_check_in_viewport (view); - e_html_editor_selection_scroll_to_caret (selection); - - e_html_editor_view_register_input_event_listener_on_body (view); -} - -static void -e_html_editor_settings_changed_cb (GSettings *settings, - const gchar *key, - EHTMLEditorView *view) -{ - GVariant *new_value, *old_value; - - new_value = g_settings_get_value (settings, key); - old_value = g_hash_table_lookup (view->priv->old_settings, key); - - if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) { - if (new_value) - g_hash_table_insert (view->priv->old_settings, g_strdup (key), new_value); - else - g_hash_table_remove (view->priv->old_settings, key); - - e_html_editor_view_update_fonts (view); - } else if (new_value) { - g_variant_unref (new_value); - } -} - -/** - * e_html_editor_view_new: - * - * Returns a new instance of the editor. - * - * Returns: A newly created #EHTMLEditorView. [transfer-full] - */ -EHTMLEditorView * -e_html_editor_view_new (void) -{ - return g_object_new (E_TYPE_HTML_EDITOR_VIEW, NULL); -} - -/** - * e_html_editor_view_get_selection: - * @view: an #EHTMLEditorView - * - * Returns an #EHTMLEditorSelection object which represents current selection or - * cursor position within the editor document. The #EHTMLEditorSelection allows - * programmer to manipulate with formatting, selection, styles etc. - * - * Returns: An always valid #EHTMLEditorSelection object. The object is owned by - * the @view and should never be free'd. - */ -EHTMLEditorSelection * -e_html_editor_view_get_selection (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL); - - return view->priv->selection; -} - -/** - * e_html_editor_view_exec_command: - * @view: an #EHTMLEditorView - * @command: an #EHTMLEditorViewCommand to execute - * @value: value of the command (or @NULL if the command does not require value) - * - * The function will fail when @value is @NULL or empty but the current @command - * requires a value to be passed. The @value is ignored when the @command does - * not expect any value. - * - * Returns: @TRUE when the command was succesfully executed, @FALSE otherwise. - */ -gboolean -e_html_editor_view_exec_command (EHTMLEditorView *view, - EHTMLEditorViewCommand command, - const gchar *value) -{ - WebKitDOMDocument *document; - const gchar *cmd_str = 0; - gboolean has_value = FALSE; - - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - -#define CHECK_COMMAND(cmd,str,val) case cmd:\ - if (val) {\ - g_return_val_if_fail (value && *value, FALSE);\ - }\ - has_value = val; \ - cmd_str = str;\ - break; - - switch (command) { - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR, "BackColor", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_BOLD, "Bold", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_COPY, "Copy", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK, "CreateLink", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_CUT, "Cut", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, "DefaultParagraphSeparator", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_DELETE, "Delete", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING, "FindString", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME, "FontName", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE, "FontSize", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA, "FontSizeDelta", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR, "ForeColor", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK, "FormatBlock", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE, "ForwardDelete", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR, "HiliteColor", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INDENT, "Indent", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE, "InsertHorizontalRule", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, "InsertHTML", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE, "InsertImage", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK, "InsertLineBreak", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, "InsertNewlineInQuotedContent", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST, "InsertOrderedList", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH, "InsertParagraph", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "InsertText", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST, "InsertUnorderedList", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_ITALIC, "Italic", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER, "JustifyCenter", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL, "JustifyFull", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT, "JustifyLeft", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE, "JustifyNone", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT, "JustifyRight", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_OUTDENT, "Outdent", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE, "Paste", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE, "PasteAndMatchStyle", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT, "PasteAsPlainText", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PRINT, "Print", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_REDO, "Redo", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT, "RemoveFormat", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL, "SelectAll", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, "Strikethrough", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, "StyleWithCSS", TRUE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT, "Subscript", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT, "Superscript", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE, "Transpose", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, "Underline", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNDO, "Undo", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNLINK, "Unlink", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNSELECT, "Unselect", FALSE) - CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_USE_CSS, "UseCSS", TRUE) - } - - view->priv->dont_save_history_in_body_input = TRUE; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - return webkit_dom_document_exec_command ( - document, cmd_str, FALSE, has_value ? value : "" ); -} - -/** - * e_html_editor_view_get_changed: - * @view: an #EHTMLEditorView - * - * Whether content of the editor has been changed. - * - * Returns: @TRUE when document was changed, @FALSE otherwise. - */ -gboolean -e_html_editor_view_get_changed (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->changed; -} - -/** - * e_html_editor_view_set_changed: - * @view: an #EHTMLEditorView - * @changed: whether document has been changed or not - * - * Sets whether document has been changed or not. The editor is tracking changes - * automatically, but sometimes it's necessary to change the dirty flag to force - * "Save changes" dialog for example. - */ -void -e_html_editor_view_set_changed (EHTMLEditorView *view, - gboolean changed) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (view->priv->changed == changed) - return; - - view->priv->changed = changed; - - g_object_notify (G_OBJECT (view), "changed"); -} - -/** - * e_html_editor_view_get_html_mode: - * @view: an #EHTMLEditorView - * - * Whether the editor is in HTML mode or plain text mode. In HTML mode, - * more formatting options are avilable an the email is sent as - * multipart/alternative. - * - * Returns: @TRUE when HTML mode is enabled, @FALSE otherwise. - */ -gboolean -e_html_editor_view_get_html_mode (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->html_mode; -} - -static gint -get_indentation_level (WebKitDOMElement *element) -{ - WebKitDOMElement *parent; - gint level = 1; - - if (!element) - return 0; - - parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element)); - /* Count level of indentation */ - while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - if (element_has_class (parent, "-x-evo-indented")) - level++; - - parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent)); - } - - return level; -} - -static void -process_indented_element (WebKitDOMElement *element) -{ - gchar *spaces; - WebKitDOMNode *child; - - if (!element) - return; - - spaces = g_strnfill (4 * get_indentation_level (element), ' '); - - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)); - while (child) { - /* If next sibling is indented blockqoute skip it, - * it will be processed afterwards */ - if (WEBKIT_DOM_IS_ELEMENT (child) && - element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-indented")) - child = webkit_dom_node_get_next_sibling (child); - - if (WEBKIT_DOM_IS_TEXT (child)) { - gchar *text_content; - gchar *indented_text; - - text_content = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (child)); - indented_text = g_strconcat (spaces, text_content, NULL); - - webkit_dom_text_replace_whole_text ( - WEBKIT_DOM_TEXT (child), - indented_text, - NULL); - - g_free (text_content); - g_free (indented_text); - } - - if (!child) - break; - - /* Move to next node */ - if (webkit_dom_node_has_child_nodes (child)) - child = webkit_dom_node_get_first_child (child); - else if (webkit_dom_node_get_next_sibling (child)) - child = webkit_dom_node_get_next_sibling (child); - else { - if (webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (element), child)) - break; - - child = webkit_dom_node_get_parent_node (child); - if (child) - child = webkit_dom_node_get_next_sibling (child); - } - } - g_free (spaces); - - webkit_dom_element_remove_attribute (element, "style"); -} - -static void -process_quote_nodes (WebKitDOMElement *blockquote) -{ - WebKitDOMNodeList *list; - int jj, length; - - /* Replace quote nodes with symbols */ - list = webkit_dom_element_query_selector_all ( - blockquote, "span.-x-evo-quoted", NULL); - length = webkit_dom_node_list_get_length (list); - for (jj = 0; jj < length; jj++) { - WebKitDOMNode *quoted_node; - gchar *text_content; - - quoted_node = webkit_dom_node_list_item (list, jj); - text_content = webkit_dom_node_get_text_content (quoted_node); - webkit_dom_html_element_set_outer_html ( - WEBKIT_DOM_HTML_ELEMENT (quoted_node), text_content, NULL); - - g_free (text_content); - g_object_unref (quoted_node); - } - g_object_unref (list); -} - -/* Taken from GtkHTML */ -static gchar * -get_alpha_value (gint value, - gboolean lower) -{ - GString *str; - gchar *rv; - gint add = lower ? 'a' : 'A'; - - str = g_string_new (". "); - - do { - g_string_prepend_c (str, ((value - 1) % 26) + add); - value = (value - 1) / 26; - } while (value); - - rv = str->str; - g_string_free (str, FALSE); - - return rv; -} - -/* Taken from GtkHTML */ -static gchar * -get_roman_value (gint value, - gboolean lower) -{ - GString *str; - const gchar *base = "IVXLCDM"; - gchar *rv; - gint b, r, add = lower ? 'a' - 'A' : 0; - - if (value > 3999) - return g_strdup ("?. "); - - str = g_string_new (". "); - - for (b = 0; value > 0 && b < 7 - 1; b += 2, value /= 10) { - r = value % 10; - if (r != 0) { - if (r < 4) { - for (; r; r--) - g_string_prepend_c (str, base[b] + add); - } else if (r == 4) { - g_string_prepend_c (str, base[b + 1] + add); - g_string_prepend_c (str, base[b] + add); - } else if (r == 5) { - g_string_prepend_c (str, base[b + 1] + add); - } else if (r < 9) { - for (; r > 5; r--) - g_string_prepend_c (str, base[b] + add); - g_string_prepend_c (str, base[b + 1] + add); - } else if (r == 9) { - g_string_prepend_c (str, base[b + 2] + add); - g_string_prepend_c (str, base[b] + add); - } - } - } - - rv = str->str; - g_string_free (str, FALSE); - - return rv; -} - -static void -process_list_to_plain_text (EHTMLEditorView *view, - WebKitDOMElement *element, - gint level, - GString *output) -{ - EHTMLEditorSelectionBlockFormat format; - EHTMLEditorSelectionAlignment alignment; - gint counter = 1; - gboolean empty = TRUE; - gchar *indent_per_level = g_strnfill (SPACES_PER_LIST_LEVEL, ' '); - WebKitDOMNode *item; - gint word_wrap_length = e_html_editor_selection_get_word_wrap_length ( - e_html_editor_view_get_selection (view)); - - format = get_list_format_from_node (WEBKIT_DOM_NODE (element)); - - /* Process list items to plain text */ - item = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)); - while (item) { - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (item)) - g_string_append (output, "\n"); - - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) { - gchar *space, *item_str = NULL; - gint ii = 0; - WebKitDOMElement *wrapped; - GString *item_value = g_string_new (""); - - empty = FALSE; - - alignment = e_html_editor_selection_get_list_alignment_from_node ( - WEBKIT_DOM_NODE (item)); - - wrapped = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (item), ".-x-evo-wrap-br", NULL); - /* Wrapped text */ - if (wrapped) { - WebKitDOMNode *node = webkit_dom_node_get_first_child (item); - GString *line = g_string_new (""); - while (node) { - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (node) && - element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) { - g_string_append (line, "\n"); - /* put spaces before line characters -> wordwraplength - indentation */ - for (ii = 0; ii < level; ii++) - g_string_append (line, indent_per_level); - if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (element)) - g_string_append (line, indent_per_level); - g_string_append (item_value, line->str); - g_string_erase (line, 0, -1); - } else { - /* append text from node to line */ - gchar *text_content; - text_content = webkit_dom_node_get_text_content (node); - g_string_append (line, text_content); - g_free (text_content); - } - node = webkit_dom_node_get_next_sibling (node); - } - - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT) - g_string_append (item_value, line->str); - - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) { - gchar *fill = NULL; - gint fill_length; - - fill_length = word_wrap_length - g_utf8_strlen (line->str, -1); - fill_length -= ii * SPACES_PER_LIST_LEVEL; - if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (element)) - fill_length += SPACES_PER_LIST_LEVEL; - fill_length /= 2; - - if (fill_length < 0) - fill_length = 0; - - fill = g_strnfill (fill_length, ' '); - - g_string_append (item_value, fill); - g_string_append (item_value, line->str); - g_free (fill); - } - - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) { - gchar *fill = NULL; - gint fill_length; - - fill_length = word_wrap_length - g_utf8_strlen (line->str, -1); - fill_length -= ii * SPACES_PER_LIST_LEVEL; - - if (fill_length < 0) - fill_length = 0; - - fill = g_strnfill (fill_length, ' '); - - g_string_append (item_value, fill); - g_string_append (item_value, line->str); - g_free (fill); - } - g_string_free (line, TRUE); - /* that same here */ - } else { - gchar *text_content = - webkit_dom_node_get_text_content (item); - g_string_append (item_value, text_content); - g_free (text_content); - } - - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST) { - space = g_strnfill (SPACES_PER_LIST_LEVEL - 2, ' '); - item_str = g_strdup_printf ( - "%s* %s", space, item_value->str); - g_free (space); - } - - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST) { - gint length = 1, tmp = counter; - - while ((tmp = tmp / 10) > 1) - length++; - - if (tmp == 1) - length++; - - space = g_strnfill (SPACES_ORDERED_LIST_FIRST_LEVEL - 2 - length, ' '); - item_str = g_strdup_printf ( - "%s%d. %s", space, counter, item_value->str); - g_free (space); - } - - if (format > E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST) { - gchar *value; - - if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA) - value = get_alpha_value (counter, FALSE); - else - value = get_roman_value (counter, FALSE); - - space = g_strnfill (SPACES_ORDERED_LIST_FIRST_LEVEL - strlen (value), ' '); - item_str = g_strdup_printf ( - "%s%s%s", space, value, item_value->str); - g_free (space); - g_free (value); - } - - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT) { - for (ii = 0; ii < level - 1; ii++) { - g_string_append (output, indent_per_level); - } - if (WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (element)) - if (e_html_editor_dom_node_find_parent_element (item, "OL")) - g_string_append (output, indent_per_level); - g_string_append (output, item_str); - } - - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) { - if (!wrapped) { - gchar *fill = NULL; - gint fill_length; - - fill_length = word_wrap_length - g_utf8_strlen (item_str, -1); - fill_length -= ii * SPACES_PER_LIST_LEVEL; - - if (fill_length < 0) - fill_length = 0; - - if (g_str_has_suffix (item_str, " ")) - fill_length++; - - fill = g_strnfill (fill_length, ' '); - - g_string_append (output, fill); - g_free (fill); - } - if (g_str_has_suffix (item_str, " ")) - g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 1); - else - g_string_append (output, item_str); - } - - if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) { - if (!wrapped) { - gchar *fill = NULL; - gint fill_length = 0; - - for (ii = 0; ii < level - 1; ii++) - g_string_append (output, indent_per_level); - - fill_length = word_wrap_length - g_utf8_strlen (item_str, -1); - fill_length -= ii * SPACES_PER_LIST_LEVEL; - if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (element)) - fill_length += SPACES_PER_LIST_LEVEL; - fill_length /= 2; - - if (fill_length < 0) - fill_length = 0; - - if (g_str_has_suffix (item_str, " ")) - fill_length++; - - fill = g_strnfill (fill_length, ' '); - - g_string_append (output, fill); - g_free (fill); - } - if (g_str_has_suffix (item_str, " ")) - g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 1); - else - g_string_append (output, item_str); - } - - counter++; - item = webkit_dom_node_get_next_sibling (item); - if (item) - g_string_append (output, "\n"); - - g_free (item_str); - g_string_free (item_value, TRUE); - } else if (node_is_list (item)) { - process_list_to_plain_text ( - view, WEBKIT_DOM_ELEMENT (item), level + 1, output); - item = webkit_dom_node_get_next_sibling (item); - } else { - item = webkit_dom_node_get_next_sibling (item); - } - } - - if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)) && !empty) - g_string_append (output, "\n"); - - g_free (indent_per_level); -} - -static void -remove_base_attributes (WebKitDOMElement *element) -{ - webkit_dom_element_remove_attribute (element, "class"); - webkit_dom_element_remove_attribute (element, "id"); - webkit_dom_element_remove_attribute (element, "name"); -} - -static void -remove_evolution_attributes (WebKitDOMElement *element) -{ - webkit_dom_element_remove_attribute (element, "data-converted"); - webkit_dom_element_remove_attribute (element, "data-edit-as-new"); - webkit_dom_element_remove_attribute (element, "data-evo-draft"); - webkit_dom_element_remove_attribute (element, "data-inline"); - webkit_dom_element_remove_attribute (element, "data-uri"); - webkit_dom_element_remove_attribute (element, "data-message"); - webkit_dom_element_remove_attribute (element, "data-name"); - webkit_dom_element_remove_attribute (element, "data-new-message"); - webkit_dom_element_remove_attribute (element, "data-user-wrapped"); - webkit_dom_element_remove_attribute (element, "data-evo-plain-text"); - webkit_dom_element_remove_attribute (element, "data-plain-text-style"); - webkit_dom_element_remove_attribute (element, "data-style"); - webkit_dom_element_remove_attribute (element, "spellcheck"); -} - -static void -convert_element_from_html_to_plain_text (EHTMLEditorView *view, - WebKitDOMElement *element, - gboolean *wrap, - gboolean *quote) -{ - gint blockquotes_count, ii, length; - gchar *inner_text, *inner_html; - WebKitDOMDocument *document; - WebKitDOMElement *top_signature, *signature, *blockquote, *main_blockquote; - WebKitDOMNode *signature_clone, *from; - WebKitDOMNodeList *list; - - document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); - - top_signature = webkit_dom_element_query_selector ( - element, ".-x-evo-top-signature", NULL); - signature = webkit_dom_element_query_selector ( - element, "span.-x-evo-signature", NULL); - main_blockquote = webkit_dom_element_query_selector ( - element, "#-x-evo-main-cite", NULL); - - blockquote = webkit_dom_document_create_element ( - document, "blockquote", NULL); - - if (main_blockquote) { - webkit_dom_element_set_attribute ( - blockquote, "type", "cite", NULL); - from = WEBKIT_DOM_NODE (main_blockquote); - } else { - if (signature) { - WebKitDOMNode *parent = webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (signature)); - signature_clone = webkit_dom_node_clone_node (parent, TRUE); - remove_node (parent); - } - from = WEBKIT_DOM_NODE (element); - } - - blockquotes_count = create_text_markers_for_citations_in_element (WEBKIT_DOM_ELEMENT (from)); - create_text_markers_for_selection_in_element (WEBKIT_DOM_ELEMENT (from)); - - /* Add the missing BR elements on the end of all DIV elements to correctly - * preserve the line breaks. */ - list = webkit_dom_element_query_selector_all (WEBKIT_DOM_ELEMENT (from), "div", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (list, ii); - if (!WEBKIT_DOM_IS_HTMLBR_ELEMENT (webkit_dom_node_get_last_child (node))) { - webkit_dom_node_append_child ( - node, - WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), - NULL); - } - g_object_unref (node); - } - g_object_unref (list); - - inner_text = webkit_dom_html_element_get_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (from)); - - webkit_dom_html_element_set_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (blockquote), inner_text, NULL); - - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (blockquote)); - - parse_html_into_blocks ( - view, document, - main_blockquote ? blockquote : WEBKIT_DOM_ELEMENT (element), - NULL, - inner_html); - - if (main_blockquote) { - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (main_blockquote)), - WEBKIT_DOM_NODE (blockquote), - WEBKIT_DOM_NODE (main_blockquote), - NULL); - - remove_evolution_attributes (WEBKIT_DOM_ELEMENT (element)); - } else { - WebKitDOMNode *first_child; - - if (signature) { - if (!top_signature) { - signature_clone = webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), - signature_clone, - NULL); - } else { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (element), - signature_clone, - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (element)), - NULL); - } - } - - first_child = webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (element)); - if (first_child) { - if (!webkit_dom_node_has_child_nodes (first_child)) { - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (first_child), - "<br>", - NULL); - } - add_selection_markers_into_element_start ( - document, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL); - } - } - - if (wrap) - *wrap = TRUE; - if (quote) - *quote = main_blockquote || blockquotes_count > 0; - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (element), "data-converted", "", NULL); - - g_free (inner_text); - g_free (inner_html); -} - -static void -process_node_to_plain_text_changing_composer_mode (EHTMLEditorView *view, - WebKitDOMNode *source) -{ - WebKitDOMElement *element; - WebKitDOMNamedNodeMap *attributes; - gint length, ii; - - attributes = webkit_dom_element_get_attributes ( - WEBKIT_DOM_ELEMENT (source)); - length = webkit_dom_named_node_map_get_length (attributes); - for (ii = 0; ii < length; ii++) { - gchar *name = NULL; - WebKitDOMNode *attribute; - - attribute = webkit_dom_named_node_map_item (attributes, ii); - - name = webkit_dom_node_get_local_name (attribute); - - if (g_strcmp0 (name, "bgcolor") == 0 || - g_strcmp0 (name, "text") == 0 || - g_strcmp0 (name, "vlink") == 0 || - g_strcmp0 (name, "link") == 0) { - - webkit_dom_element_remove_attribute_node ( - WEBKIT_DOM_ELEMENT (source), - WEBKIT_DOM_ATTR (attribute), - NULL); - length--; - } - g_free (name); - g_object_unref (attribute); - } - g_object_unref (attributes); - - /* Signature */ - element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (source), "div.-x-evo-signature-wrapper", NULL); - if (element) { - WebKitDOMNode *first_child; - - first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)); - - convert_element_from_html_to_plain_text ( - view, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL); - } -} - -/* This function is different than the others there as this needs to go through - * the DOM node by node and generate the plain text of their content. For some - * it will just take the text content, but for example the lists are not that - * easy. */ -static void -process_node_to_plain_text_for_exporting (EHTMLEditorView *view, - WebKitDOMNode *source, - GString *buffer) -{ - WebKitDOMNodeList *nodes = NULL; - gchar *content = NULL; - gint ii, nodes_length; - - nodes = webkit_dom_node_get_child_nodes (source); - nodes_length = webkit_dom_node_list_get_length (nodes); - - for (ii = 0; ii < nodes_length; ii++) { - WebKitDOMNode *child; - gboolean skip_node = FALSE; - - child = webkit_dom_node_list_item (nodes, ii); - - if (WEBKIT_DOM_IS_TEXT (child)) { - gchar *class; - const gchar *css_align = NULL; - GRegex *regex; - - content = webkit_dom_node_get_text_content (child); - if (strstr (content, UNICODE_ZERO_WIDTH_SPACE)) { - gchar *tmp; - - regex = g_regex_new (UNICODE_ZERO_WIDTH_SPACE, 0, 0, NULL); - tmp = g_regex_replace ( - regex, content, -1, 0, "", 0, NULL); - g_free (content); - content = tmp; - g_regex_unref (regex); - } - - class = webkit_dom_element_get_class_name (WEBKIT_DOM_ELEMENT (source)); - if ((css_align = strstr (class, "-x-evo-align-"))) { - gchar *align; - gchar *content_with_align; - gint length; - gint word_wrap_length = - e_html_editor_selection_get_word_wrap_length ( - e_html_editor_view_get_selection (view)); - - if (!g_str_has_prefix (css_align + 13, "left")) { - if (g_str_has_prefix (css_align + 13, "center")) - length = (word_wrap_length - g_utf8_strlen (content, -1)) / 2; - else - length = word_wrap_length - g_utf8_strlen (content, -1); - - if (length < 0) - length = 0; - - if (g_str_has_suffix (content, " ")) { - char *tmp; - - length++; - align = g_strnfill (length, ' '); - - tmp = g_strndup (content, g_utf8_strlen (content, -1) -1); - - content_with_align = g_strconcat ( - align, tmp, NULL); - g_free (tmp); - } else { - align = g_strnfill (length, ' '); - - content_with_align = g_strconcat ( - align, content, NULL); - } - - g_free (content); - g_free (align); - content = content_with_align; - } - } - - g_free (class); - - g_string_append (buffer, content); - - g_free (content); - - goto next; - } - - if (!WEBKIT_DOM_IS_ELEMENT (child)) - goto next; - - if (element_has_class (WEBKIT_DOM_ELEMENT (child), "Apple-tab-span")) { - content = webkit_dom_node_get_text_content (child); - g_string_append (buffer, content); - g_free (content); - skip_node = TRUE; - goto next; - } - - if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child)) - process_quote_nodes (WEBKIT_DOM_ELEMENT (child)); - - if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child) && - element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-indented")) { - process_indented_element (WEBKIT_DOM_ELEMENT (child)); - } - - if (node_is_list (child)) { - process_list_to_plain_text ( - view, WEBKIT_DOM_ELEMENT (child), 1, buffer); - skip_node = TRUE; - goto next; - } - - if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-resizable-wrapper") && - !element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) { - skip_node = TRUE; - goto next; - } - - /* Signature */ - if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child) && - element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-signature-wrapper")) { - WebKitDOMNode *first_child; - gchar *id; - - first_child = webkit_dom_node_get_first_child (child); - - skip_node = TRUE; - /* Don't generate any text if the signature is set to None. */ - - id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child)); - if (g_strcmp0 (id, "none") == 0) { - g_free (id); - - remove_node (child); - goto next; - } - g_free (id); - - if (view->priv->html_mode) { - convert_element_from_html_to_plain_text ( - view, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL); - skip_node = FALSE; - } - - goto next; - } - - /* Replace smileys with their text representation */ - if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) { - WebKitDOMNode *text_version; - - text_version = webkit_dom_node_get_last_child (child); - content = webkit_dom_html_element_get_inner_text ( - WEBKIT_DOM_HTML_ELEMENT (text_version)); - g_string_append (buffer, content); - g_free (content); - skip_node = TRUE; - goto next; - } - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) { - g_string_append (buffer, "\n"); - goto next; - } - - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (child)) { - content = webkit_dom_html_element_get_inner_text (WEBKIT_DOM_HTML_ELEMENT (child)); - g_string_append (buffer, content); - g_free (content); - skip_node = TRUE; - } - next: - if (!skip_node && webkit_dom_node_has_child_nodes (child)) - process_node_to_plain_text_for_exporting (view, child, buffer); - g_object_unref (child); - } - g_object_unref (nodes); - - if (!g_str_has_suffix (buffer->str, "\n") && - (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (source) || - WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (source) || - WEBKIT_DOM_IS_HTML_PRE_ELEMENT (source) || - WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (source))) - g_string_append (buffer, "\n"); - - if (g_str_has_suffix (buffer->str, "\n") && - WEBKIT_DOM_IS_HTML_BODY_ELEMENT (source)) - g_string_truncate (buffer, buffer->len - 1); -} - -static void -process_node_to_html_changing_composer_mode (EHTMLEditorView *view, - WebKitDOMNode *source) -{ -} - -static void -process_node_to_html_for_exporting (EHTMLEditorView *view, - WebKitDOMNode *source) -{ - WebKitDOMNodeList *list = NULL; - WebKitDOMElement *element; - WebKitDOMDocument *document; - gint ii, length; - - document = webkit_dom_node_get_owner_document (source); - - remove_evolution_attributes (WEBKIT_DOM_ELEMENT (source)); - - /* Aligned elements */ - list = webkit_dom_element_query_selector_all (WEBKIT_DOM_ELEMENT (source), "[class^=\"-x-evo-align\"]", NULL); - length = webkit_dom_node_list_get_length (list); - - for (ii = 0; ii < length; ii++) { - gchar *class = NULL; - const gchar *align_type; - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (list, ii); - class = webkit_dom_element_get_class_name (WEBKIT_DOM_ELEMENT (node)); - align_type = class + 13; - if (!g_str_has_prefix (align_type, "left")) { - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node)) - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (node), - "style", - g_str_has_prefix (align_type, "center") ? - "list-style-position: inside; text-align: center" : - "list-style-position: inside; text-align: right", - NULL); - else - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (node), - "style", - g_str_has_prefix (align_type + 13, "center") ? - "text-align: center" : - "text-align: right", - NULL); - } - element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-left"); - element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-center"); - element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-right"); - g_free (class); - g_object_unref (node); - } - g_object_unref (list); - - /* Indented elements */ - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (source), ".-x-evo-indented", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (list, ii); - element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-indented"); - remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node)); - - g_object_unref (node); - } - g_object_unref (list); - - /* Tab characters */ - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (source), ".Apple-tab-span", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - gchar *text_content; - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (list, ii); - text_content = webkit_dom_node_get_text_content (node); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (webkit_dom_document_create_text_node (document, text_content)), - node, - NULL); - - remove_node (node); - g_object_unref (node); - } - g_object_unref (list); - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (source), ".-x-evo-quoted", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *quoted_node; - gchar *text_content; - - quoted_node = webkit_dom_node_list_item (list, ii); - text_content = webkit_dom_node_get_text_content (quoted_node); - webkit_dom_html_element_set_outer_html ( - WEBKIT_DOM_HTML_ELEMENT (quoted_node), text_content, NULL); - - g_free (text_content); - g_object_unref (quoted_node); - } - g_object_unref (list); - - /* Images */ - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (source), ".-x-evo-resizable-wrapper:not(.-x-evo-smiley-wrapper)", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node, *image; - - node = webkit_dom_node_list_item (list, ii); - image = webkit_dom_node_get_first_child (node); - - if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (image)) { - remove_evolution_attributes ( - WEBKIT_DOM_ELEMENT (image)); - - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (node), image, node, NULL); - } - - g_object_unref (node); - } - g_object_unref (list); - - /* Signature */ - element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (source), "div.-x-evo-signature-wrapper", NULL); - if (element) { - WebKitDOMNode *first_child; - gchar *id; - - /* Don't generate any text if the signature is set to None. */ - first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)); - id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child)); - if (g_strcmp0 (id, "none") == 0) { - remove_node (WEBKIT_DOM_NODE (element)); - } else { - remove_base_attributes (element); - remove_base_attributes (WEBKIT_DOM_ELEMENT (first_child)); - remove_evolution_attributes (WEBKIT_DOM_ELEMENT (first_child)); - } - g_free (id); - } - - /* Smileys */ - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (source), "-x-evo-smiley-wrapper", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - WebKitDOMElement *img; - - node = webkit_dom_node_list_item (list, ii); - img = WEBKIT_DOM_ELEMENT (webkit_dom_node_get_first_child (node)); - - remove_evolution_attributes (img); - remove_base_attributes (img); - - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (img), - node, - NULL); - - g_object_unref (node); - } - g_object_unref (list); - - list = webkit_dom_element_get_elements_by_tag_name ( - WEBKIT_DOM_ELEMENT (source), "pre"); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (list, ii); - remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node)); - g_object_unref (node); - } - g_object_unref (list); - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (source), ".-x-evo-paragraph", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (list, ii); - element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-paragraph"); - g_object_unref (node); - } - g_object_unref (list); -} - -void -remove_image_attributes_from_element (WebKitDOMElement *element) -{ - webkit_dom_element_remove_attribute (element, "background"); - webkit_dom_element_remove_attribute (element, "data-uri"); - webkit_dom_element_remove_attribute (element, "data-inline"); - webkit_dom_element_remove_attribute (element, "data-name"); -} - -static void -remove_background_images_in_element (WebKitDOMElement *element) -{ - gint length, ii; - WebKitDOMNodeList *images; - - images = webkit_dom_element_query_selector_all ( - element, "[background][data-inline]", NULL); - - length = webkit_dom_node_list_get_length (images); - for (ii = 0; ii < length; ii++) { - WebKitDOMElement *image = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_list_item (images, ii)); - - remove_image_attributes_from_element (image); - g_object_unref (image); - } - g_object_unref (images); - - remove_image_attributes_from_element (element); -} - -static void -remove_images_in_element (WebKitDOMElement *element) -{ - gint length, ii; - WebKitDOMNodeList *images; - - images = webkit_dom_element_query_selector_all ( - element, "img:not(.-x-evo-smiley-img)", NULL); - - length = webkit_dom_node_list_get_length (images); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (images, ii); - remove_node_and_parents_if_empty (node); - g_object_unref (node); - } - g_object_unref (images); -} - -static void -remove_images (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - remove_images_in_element ( - WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document))); -} - -static void -toggle_smileys (EHTMLEditorView *view) -{ - gboolean html_mode; - gint length; - gint ii; - WebKitDOMDocument *document; - WebKitDOMNodeList *smileys; - - html_mode = e_html_editor_view_get_html_mode (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - smileys = webkit_dom_document_query_selector_all ( - document, "img.-x-evo-smiley-img", NULL); - - length = webkit_dom_node_list_get_length (smileys); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *img = webkit_dom_node_list_item (smileys, ii); - WebKitDOMNode *text = webkit_dom_node_get_next_sibling (img); - WebKitDOMElement *parent = webkit_dom_node_get_parent_element (img); - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (html_mode ? text : img), - "style", - "display: none", - NULL); - - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (html_mode ? img : text), "style"); - - if (html_mode) - element_add_class (parent, "-x-evo-resizable-wrapper"); - else - element_remove_class (parent, "-x-evo-resizable-wrapper"); - g_object_unref (img); - } - - g_object_unref (smileys); -} - -static void -toggle_paragraphs_style_in_element (EHTMLEditorView *view, - WebKitDOMElement *element, - gboolean html_mode) -{ - EHTMLEditorSelection *selection; - gint ii, length; - WebKitDOMNodeList *paragraphs; - - selection = e_html_editor_view_get_selection (view); - - paragraphs = webkit_dom_element_query_selector_all ( - element, ":not(td) > .-x-evo-paragraph", NULL); - - length = webkit_dom_node_list_get_length (paragraphs); - - for (ii = 0; ii < length; ii++) { - gchar *style; - const gchar *css_align; - WebKitDOMNode *node = webkit_dom_node_list_item (paragraphs, ii); - - if (html_mode) { - style = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (node), "style"); - - if ((css_align = strstr (style, "text-align: "))) { - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (node), - "style", - g_str_has_prefix (css_align + 12, "center") ? - "text-align: center" : - "text-align: right", - NULL); - } else { - /* In HTML mode the paragraphs don't have width limit */ - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (node), "style"); - } - g_free (style); - } else { - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node (node); - /* If the paragraph is inside indented paragraph don't set - * the style as it will be inherited */ - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent) && node_is_list (node)) { - gint offset; - - offset = WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node) ? - SPACES_PER_LIST_LEVEL : SPACES_ORDERED_LIST_FIRST_LEVEL; - /* In plain text mode the paragraphs have width limit */ - e_html_editor_selection_set_paragraph_style ( - selection, WEBKIT_DOM_ELEMENT (node), -1, -offset, ""); - } else if (!element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) { - const gchar *style_to_add = ""; - style = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (node), "style"); - - if ((css_align = strstr (style, "text-align: "))) { - style_to_add = g_str_has_prefix ( - css_align + 12, "center") ? - "text-align: center;" : - "text-align: right;"; - } - - /* In plain text mode the paragraphs have width limit */ - e_html_editor_selection_set_paragraph_style ( - selection, WEBKIT_DOM_ELEMENT (node), - -1, 0, style_to_add); - - g_free (style); - } - } - g_object_unref (node); - } - g_object_unref (paragraphs); -} - -static void -toggle_paragraphs_style (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - toggle_paragraphs_style_in_element ( - view, - WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)), - view->priv->html_mode); -} - -static gchar * -process_content_for_saving_as_draft (EHTMLEditorView *view, - gboolean only_inner_body) -{ - gchar *content; - gint ii, length; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - WebKitDOMElement *document_element; - WebKitDOMNodeList *list; - WebKitDOMNode *document_element_clone; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-evo-draft", "", NULL); - - document_element = webkit_dom_document_get_document_element (document); - - document_element_clone = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (document_element), TRUE); - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (document_element_clone), "a.-x-evo-visited-link", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *anchor; - - anchor = webkit_dom_node_list_item (list, ii); - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (anchor), "class"); - g_object_unref (anchor); - } - g_object_unref (list); - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (document_element_clone), "#-x-evo-input-start", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (list, ii); - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "id"); - g_object_unref (node); - } - - g_object_unref (list); - - if (only_inner_body) { - WebKitDOMElement *body; - WebKitDOMNode *first_child; - - body = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (document_element_clone), "body", NULL); - - first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - if (!view->priv->html_mode) - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (first_child), "data-evo-signature-plain-text-mode", "", NULL); - - content = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (body)); - - if (!view->priv->html_mode) - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (first_child), "data-evo-signature-plain-text-mode"); - } else - content = webkit_dom_html_element_get_outer_html ( - WEBKIT_DOM_HTML_ELEMENT (document_element_clone)); - - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-evo-draft"); - - return content; -} - -static void -toggle_indented_elements (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMNodeList *list; - gint ii, length; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - list = webkit_dom_document_query_selector_all (document, ".-x-evo-indented", NULL); - length = webkit_dom_node_list_get_length (list); - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - if (view->priv->html_mode) - dom_element_swap_attributes (WEBKIT_DOM_ELEMENT (node), "style", "data-plain-text-style"); - else - dom_element_swap_attributes (WEBKIT_DOM_ELEMENT (node), "data-plain-text-style", "style"); - g_object_unref (node); - } - g_object_unref (list); -} - -static void -toggle_tables (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMNodeList *list; - gint ii, length; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - list = webkit_dom_document_query_selector_all (document, "table", NULL); - length = webkit_dom_node_list_get_length (list); - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *table = webkit_dom_node_list_item (list, ii); - - if (view->priv->html_mode) { - element_remove_class (WEBKIT_DOM_ELEMENT (table), "-x-evo-plaintext-table"); - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (table), "data-width", "width"); - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (table), "data-cellspacing", "cellspacing"); - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (table), "data-cellpadding", "cellpadding"); - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (table), "data-border", "border"); - } else { - element_add_class (WEBKIT_DOM_ELEMENT (table), "-x-evo-plaintext-table"); - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (table), "width", "data-width"); - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (table), "cellspacing", "data-cellspacing"); - webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (table), "cellspacing", "0", NULL); - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (table), "cellpadding", "data-cellpadding"); - webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (table), "cellpadding", "0", NULL); - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (table), "border", "data-border"); - webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (table), "border", "0", NULL); - } - g_object_unref (table); - } - g_object_unref (list); -} - -static void -toggle_unordered_lists (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMNodeList *list; - gint ii, length; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - list = webkit_dom_document_query_selector_all (document, "ul", NULL); - length = webkit_dom_node_list_get_length (list); - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - if (view->priv->html_mode) { - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (node), "data-evo-plain-text"); - } else { - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (node), "data-evo-plain-text", "", NULL); - } - g_object_unref (node); - } - g_object_unref (list); -} - -static void -process_content_to_html_changing_composer_mode (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMNode *body; - WebKitDOMElement *blockquote; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)); - - blockquote = webkit_dom_document_query_selector ( - document, "blockquote[type|=cite]", NULL); - - if (blockquote) - e_html_editor_view_dequote_plain_text (view); - - toggle_paragraphs_style (view); - toggle_smileys (view); - toggle_tables (view); - toggle_indented_elements (view); - toggle_unordered_lists (view); - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (body)); - - process_node_to_html_changing_composer_mode (view, body); -} - -static void -wrap_paragraphs_in_quoted_content (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - gint ii, length; - WebKitDOMNodeList *paragraphs; - - paragraphs = webkit_dom_document_query_selector_all ( - document, "blockquote[type=cite] > .-x-evo-paragraph", NULL); - - length = webkit_dom_node_list_get_length (paragraphs); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *paragraph; - - paragraph = webkit_dom_node_list_item (paragraphs, ii); - - e_html_editor_selection_wrap_paragraph ( - selection, WEBKIT_DOM_ELEMENT (paragraph)); - g_object_unref (paragraph); - } - g_object_unref (paragraphs); -} - -static void -process_content_to_plain_text_changing_composer_mode (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMNode *body, *head, *node; - WebKitDOMElement *blockquote; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)); - head = WEBKIT_DOM_NODE (webkit_dom_document_get_head (document)); - - while ((node = webkit_dom_node_get_last_child (head))) - remove_node (node); - - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-user-colors"); - - e_html_editor_selection_save (view->priv->selection); - - blockquote = webkit_dom_document_query_selector ( - document, "blockquote[type|=cite]", NULL); - - if (blockquote) { - wrap_paragraphs_in_quoted_content (view->priv->selection, document); - quote_plain_text_elements_after_wrapping_in_document (document); - } - - toggle_paragraphs_style (view); - toggle_smileys (view); - toggle_tables (view); - toggle_indented_elements (view); - toggle_unordered_lists (view); - remove_images (view); - remove_background_images_in_element (WEBKIT_DOM_ELEMENT (body)); - - process_node_to_plain_text_changing_composer_mode (view, body); - - e_html_editor_selection_restore (view->priv->selection); - e_html_editor_view_force_spell_check_in_viewport (view); - -} - -static gchar * -process_content_to_plain_text_for_exporting (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - gboolean wrap = FALSE, quote = FALSE, converted; - gint length, ii; - GString *plain_text; - WebKitDOMDocument *document; - WebKitDOMNode *body, *source; - WebKitDOMNodeList *paragraphs; - - plain_text = g_string_sized_new (1024); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)); - converted = webkit_dom_element_has_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-converted"); - source = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE); - - selection = e_html_editor_view_get_selection (view); - - e_html_editor_selection_save (selection); - - /* If composer is in HTML mode we have to move the content to plain version */ - if (view->priv->html_mode) { - if (converted || view->priv->is_new_message || view->priv->is_message_from_draft) { - toggle_paragraphs_style_in_element ( - view, WEBKIT_DOM_ELEMENT (source), FALSE); - remove_images_in_element ( - WEBKIT_DOM_ELEMENT (source)); - remove_background_images_in_element ( - WEBKIT_DOM_ELEMENT (source)); - } else { - WebKitDOMElement *div; - WebKitDOMNode *child; - - div = webkit_dom_document_create_element (document, "div", NULL); - while ((child = webkit_dom_node_get_first_child (source))) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (div), - child, - NULL); - } - - paragraphs = webkit_dom_element_query_selector_all ( - div, "#-x-evo-input-start", NULL); - - length = webkit_dom_node_list_get_length (paragraphs); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *paragraph; - - paragraph = webkit_dom_node_list_item (paragraphs, ii); - - webkit_dom_element_remove_attribute ( - WEBKIT_DOM_ELEMENT (paragraph), "id"); - g_object_unref (paragraph); - } - g_object_unref (paragraphs); - - remove_images_in_element (div); - - convert_element_from_html_to_plain_text (view, div, &wrap, "e); - - source = WEBKIT_DOM_NODE (div); - } - } - - paragraphs = webkit_dom_element_get_elements_by_class_name ( - WEBKIT_DOM_ELEMENT (source), "-x-evo-paragraph"); - - length = webkit_dom_node_list_get_length (paragraphs); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *paragraph; - - paragraph = webkit_dom_node_list_item (paragraphs, ii); - - if (node_is_list (paragraph)) { - WebKitDOMNode *item = webkit_dom_node_get_first_child (paragraph); - - while (item) { - WebKitDOMNode *next_item = - webkit_dom_node_get_next_sibling (item); - - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) { - e_html_editor_selection_wrap_paragraph ( - selection, WEBKIT_DOM_ELEMENT (item)); - } - item = next_item; - } - } else if (!webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (paragraph), ".-x-evo-wrap-br,.-x-evo-quoted", NULL)) { - /* Dont't try to wrap the already wrapped content. */ - e_html_editor_selection_wrap_paragraph ( - selection, WEBKIT_DOM_ELEMENT (paragraph)); - } - g_object_unref (paragraph); - } - g_object_unref (paragraphs); - - paragraphs = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (source), "#-x-evo-selection-start-marker, #-x-evo-selection-end-marker", NULL); - length = webkit_dom_node_list_get_length (paragraphs); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node = webkit_dom_node_list_item (paragraphs, ii); - WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); - - remove_node (node); - g_object_unref (node); - webkit_dom_node_normalize (parent); - } - g_object_unref (paragraphs); - - if (quote) { - quote_plain_text_recursive (document, source, source, 0); - } else if (view->priv->html_mode) { - WebKitDOMElement *citation; - - citation = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (source), "blockquote[type=cite]", NULL); - if (citation) - quote_plain_text_recursive (document, source, source, 0); - } - - process_node_to_plain_text_for_exporting (view, source, plain_text); - - e_html_editor_selection_restore (selection); - - /* Return text content between <body> and </body> */ - return g_string_free (plain_text, FALSE); -} - -static gchar * -process_content_to_html_for_exporting (EHTMLEditorView *view) -{ - gint ii, length; - gchar *html_content; - WebKitDOMDocument *document; - WebKitDOMElement *element; - WebKitDOMNode *node, *document_clone; - WebKitDOMNodeList *list; - gboolean send_editor_colors = FALSE; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - document_clone = webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (webkit_dom_document_get_document_element (document)), TRUE); - element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-quote-style", NULL); - if (element) - remove_node (WEBKIT_DOM_NODE (element)); - element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-a-color-style", NULL); - if (element) - remove_node (WEBKIT_DOM_NODE (element)); - element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-a-color-style-visited", NULL); - if (element) - remove_node (WEBKIT_DOM_NODE (element)); - /* When the Ctrl + Enter is pressed for sending, the links are activated. */ - element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-style-a", NULL); - if (element) - remove_node (WEBKIT_DOM_NODE (element)); - node = WEBKIT_DOM_NODE (webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (document_clone), "body", NULL)); - element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (node), "#-x-evo-selection-start-marker", NULL); - if (element) - remove_node (WEBKIT_DOM_NODE (element)); - element = webkit_dom_element_query_selector ( - WEBKIT_DOM_ELEMENT (node), "#-x-evo-selection-end-marker", NULL); - if (element) - remove_node (WEBKIT_DOM_NODE (element)); - - send_editor_colors = g_settings_get_boolean ( - view->priv->mail_settings, "composer-inherit-theme-colors"); - - if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), "data-user-colors")) { - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "data-user-colors"); - } else if (!send_editor_colors) { - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "bgcolor"); - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "text"); - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "link"); - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "vlink"); - } - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (node), "span[data-hidden-space]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *hidden_space_node; - - hidden_space_node = webkit_dom_node_list_item (list, ii); - remove_node (hidden_space_node); - g_object_unref (hidden_space_node); - } - g_object_unref (list); - - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (node), "[data-style]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *data_style_node; - - data_style_node = webkit_dom_node_list_item (list, ii); - - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (data_style_node), "data-style", "style"); - g_object_unref (data_style_node); - } - g_object_unref (list); - - process_node_to_html_for_exporting (view, node); - - html_content = webkit_dom_html_element_get_outer_html ( - WEBKIT_DOM_HTML_ELEMENT (document_clone)); - - g_object_unref (document_clone); - - return html_content; -} - -static gboolean -show_lose_formatting_dialog (EHTMLEditorView *view) -{ - gboolean lose; - GtkWidget *toplevel; - GtkWindow *parent = NULL; - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view)); - - if (GTK_IS_WINDOW (toplevel)) - parent = GTK_WINDOW (toplevel); - - lose = e_util_prompt_user ( - parent, "org.gnome.evolution.mail", "prompt-on-composer-mode-switch", - "mail-composer:prompt-composer-mode-switch", NULL); - - if (!lose) { - /* Nothing has changed, but notify anyway */ - g_object_notify (G_OBJECT (view), "html-mode"); - return FALSE; - } - - return TRUE; -} - -static void -convert_when_changing_composer_mode (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - gboolean quote = FALSE, wrap = FALSE; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - convert_element_from_html_to_plain_text ( - view, WEBKIT_DOM_ELEMENT (body), &wrap, "e); - - if (wrap) - e_html_editor_selection_wrap_paragraphs_in_document (selection, document); - - if (quote) { - e_html_editor_selection_save (selection); - if (wrap) - quote_plain_text_elements_after_wrapping_in_document ( - document); - else - body = WEBKIT_DOM_HTML_ELEMENT (e_html_editor_view_quote_plain_text (view)); - e_html_editor_selection_restore (selection); - } - - toggle_paragraphs_style (view); - toggle_smileys (view); - remove_images (view); - remove_background_images_in_element (WEBKIT_DOM_ELEMENT (body)); - - clear_attributes (document); - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-converted", "", NULL); - - /* Update fonts - in plain text we only want monospace */ - e_html_editor_view_update_fonts (view); - - e_html_editor_view_force_spell_check_in_viewport (view); -} - -void -e_html_editor_view_embed_styles (EHTMLEditorView *view) -{ - WebKitWebSettings *settings; - WebKitDOMDocument *document; - WebKitDOMElement *sheet; - gchar *stylesheet_uri; - gchar *stylesheet_content; - const gchar *stylesheet; - gsize length; - - settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view)); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - g_object_get ( - G_OBJECT (settings), - "user-stylesheet-uri", &stylesheet_uri, - NULL); - - stylesheet = strstr (stylesheet_uri, ","); - stylesheet_content = (gchar *) g_base64_decode (stylesheet, &length); - g_free (stylesheet_uri); - - if (length == 0) { - g_free (stylesheet_content); - return; - } - - e_web_view_create_and_add_css_style_sheet (document, "-x-evo-composer-sheet"); - - sheet = webkit_dom_document_get_element_by_id (document, "-x-evo-composer-sheet"); - webkit_dom_element_set_attribute ( - sheet, - "type", - "text/css", - NULL); - - webkit_dom_html_element_set_inner_html (WEBKIT_DOM_HTML_ELEMENT (sheet), stylesheet_content, NULL); - - g_free (stylesheet_content); -} - -void -e_html_editor_view_remove_embed_styles (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMElement *sheet; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - sheet = webkit_dom_document_get_element_by_id ( - document, "-x-evo-composer-sheet"); - - if (sheet) - remove_node (WEBKIT_DOM_NODE (sheet)); -} - -static void -set_link_colors_in_document (WebKitDOMDocument *document, - GdkRGBA *color, - gboolean visited) -{ - gchar *color_str = NULL; - const gchar *style_id; - guint32 color_value; - WebKitDOMHTMLHeadElement *head; - WebKitDOMHTMLElement *body; - WebKitDOMElement *style_element; - - style_id = visited ? "-x-evo-a-color-style-visited" : "-x-evo-a-color-style"; - head = webkit_dom_document_get_head (document); - body = webkit_dom_document_get_body (document); - - style_element = webkit_dom_document_get_element_by_id (document, style_id); - if (!style_element) { - style_element = webkit_dom_document_create_element (document, "style", NULL); - webkit_dom_element_set_id (style_element, style_id); - webkit_dom_element_set_attribute (style_element, "type", "text/css", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style_element), NULL); - } - - color_value = e_rgba_to_value (color); - color_str = g_strdup_printf ( - visited ? "a.-x-evo-visited-link { color: #%06x; }" : "a { color: #%06x; }", color_value); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (style_element), color_str, NULL); - g_free (color_str); - - color_str = g_strdup_printf ("#%06x", color_value); - if (visited) - webkit_dom_html_body_element_set_v_link ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body), color_str); - else - webkit_dom_html_body_element_set_link ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body), color_str); - g_free (color_str); -} - -void -e_html_editor_view_set_link_color (EHTMLEditorView *view, - GdkRGBA *color) -{ - WebKitDOMDocument *document; - - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - g_return_if_fail (color != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - set_link_colors_in_document (document, color, FALSE); -} - -void -e_html_editor_view_set_visited_link_color (EHTMLEditorView *view, - GdkRGBA *color) -{ - WebKitDOMDocument *document; - - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - g_return_if_fail (color != NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - set_link_colors_in_document (document, color, TRUE); -} - -static void -get_color_from_context (GtkStyleContext *context, - const gchar *name, - GdkRGBA *out_color) -{ - GdkColor *color = NULL; - - gtk_style_context_get_style (context, name, &color, NULL); - - if (color == NULL) { - gboolean is_visited = strstr (name, "visited") != NULL; - #if GTK_CHECK_VERSION(3,12,0) - GtkStateFlags state; - #endif - - out_color->alpha = 1; - out_color->red = is_visited ? 1 : 0; - out_color->green = 0; - out_color->blue = is_visited ? 0 : 1; - - #if GTK_CHECK_VERSION(3,12,0) - state = gtk_style_context_get_state (context); - state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK)); - state = state | (is_visited ? GTK_STATE_FLAG_VISITED : GTK_STATE_FLAG_LINK); - - gtk_style_context_save (context); - gtk_style_context_set_state (context, state); - gtk_style_context_get_color (context, state, out_color); - gtk_style_context_restore (context); - #endif - } else { - out_color->alpha = 1; - out_color->red = ((gdouble) color->red) / G_MAXUINT16; - out_color->green = ((gdouble) color->green) / G_MAXUINT16; - out_color->blue = ((gdouble) color->blue) / G_MAXUINT16; - - gdk_color_free (color); - } -} - -static void -set_link_colors (EHTMLEditorView *view) -{ - GdkRGBA rgba; - GtkStyleContext *context; - - context = gtk_widget_get_style_context (GTK_WIDGET (view)); - - get_color_from_context (context, "link-color", &rgba); - e_html_editor_view_set_link_color (view, &rgba); - - get_color_from_context (context, "visited-link-color", &rgba); - e_html_editor_view_set_visited_link_color (view, &rgba); -} - -static void -style_updated_cb (EHTMLEditorView *view) -{ - GdkRGBA color; - gchar *color_value; - GtkStateFlags state_flags; - GtkStyleContext *style_context; - gboolean backdrop; - WebKitDOMHTMLElement *body; - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (body), "data-user-colors")) { - /* If the user set the colors in Page dialog, this callback is useless. */ - return; - } - - state_flags = gtk_widget_get_state_flags (GTK_WIDGET (view)); - style_context = gtk_widget_get_style_context (GTK_WIDGET (view)); - backdrop = (state_flags & GTK_STATE_FLAG_BACKDROP) != 0; - - if (gtk_style_context_lookup_color ( - style_context, - backdrop ? "theme_unfocused_base_color" : "theme_base_color", - &color)) - color_value = g_strdup_printf ("#%06x", e_rgba_to_value (&color)); - else - color_value = g_strdup (E_UTILS_DEFAULT_THEME_BASE_COLOR); - - - webkit_dom_html_body_element_set_bg_color ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body), color_value); - - g_free (color_value); - - if (gtk_style_context_lookup_color ( - style_context, - backdrop ? "theme_unfocused_fg_color" : "theme_fg_color", - &color)) - color_value = g_strdup_printf ("#%06x", e_rgba_to_value (&color)); - else - color_value = g_strdup (E_UTILS_DEFAULT_THEME_FG_COLOR); - - webkit_dom_html_body_element_set_text ( - WEBKIT_DOM_HTML_BODY_ELEMENT (body), color_value); - - g_free (color_value); - - set_link_colors (view); -} - -static void -html_editor_view_load_status_changed (EHTMLEditorView *view) -{ - glong ii, length; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMHTMLElement *body; - WebKitDOMNodeList *list; - WebKitLoadStatus status; - - status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view)); - if (status != WEBKIT_LOAD_FINISHED) - return; - - /* Dispatch queued operations - as we are using this just for load - * operations load just the latest request and throw away the rest. */ - if (view->priv->post_reload_operations && - !g_queue_is_empty (view->priv->post_reload_operations)) { - - PostReloadOperation *op; - - op = g_queue_pop_head (view->priv->post_reload_operations); - - op->func (view, op->data); - - if (op->data_free_func) - op->data_free_func (op->data); - g_free (op); - - while ((op = g_queue_pop_head (view->priv->post_reload_operations))) { - if (op->data_free_func) - op->data_free_func (op->data); - g_free (op); - } - - g_queue_clear (view->priv->post_reload_operations); - - return; - } - - g_signal_emit (view, signals[IS_READY], 0); - view->priv->reload_in_progress = FALSE; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (body), "style"); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-message", "", NULL); - - /* Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=752997 where - * WebKit (2.4.9) crashes when it is trying to move or remove an anchor - * element that has an image element inside and this image element has - * the CSS float property set in the style attribute. To workaround it we - * will rename the style attribute and rename it back when we will send - * the message. It is unfortunate that we can change the formatting with - * this, but this is definitely better than crashing. This could be - * removed once Evolution switches to WebKit2 as the WebKit2 is unaffected - * (tested on 2.8.4). */ - list = webkit_dom_document_query_selector_all (document, "a img[style]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - gchar *style_value; - - node = webkit_dom_node_list_item (list, ii); - style_value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "style"); - if (camel_strstrcase (style_value, "float")) - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (node), "style", "data-style"); - g_free (style_value); - - g_object_unref (node); - } - g_object_unref (list); - - if (view->priv->convert_in_situ) { - html_editor_convert_view_content (view, NULL); - /* Make the quote marks non-selectable. */ - disable_quote_marks_select (document); - html_editor_view_set_links_active (view, FALSE); - style_updated_cb (view); - view->priv->convert_in_situ = FALSE; - - e_html_editor_view_register_input_event_listener_on_body (view); - register_html_events_handlers (view, body); - - return; - } - - /* Make the quote marks non-selectable. */ - disable_quote_marks_select (document); - style_updated_cb (view); - html_editor_view_set_links_active (view, FALSE); - put_body_in_citation (document); - move_elements_to_body (view); - repair_gmail_blockquotes (document); - remove_thunderbird_signature (document); - - if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (body), "data-evo-draft")) { - /* Restore the selection how it was when the draft was saved */ - e_html_editor_selection_move_caret_into_element ( - document, WEBKIT_DOM_ELEMENT (body), FALSE); - e_html_editor_selection_restore ( - e_html_editor_view_get_selection (view)); - e_html_editor_view_remove_embed_styles (view); - } - - /* The composer body could be empty in some case (loading an empty string - * or empty HTML. In that case create the initial paragraph. */ - if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))) { - EHTMLEditorSelection *selection; - WebKitDOMElement *paragraph; - - selection = e_html_editor_view_get_selection (view); - paragraph = prepare_paragraph (selection, document, TRUE); - webkit_dom_element_set_id (paragraph, "-x-evo-input-start"); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (paragraph), NULL); - e_html_editor_selection_restore (selection); - } - - /* Register on input event that is called when the content (body) is modified */ - e_html_editor_view_register_input_event_listener_on_body (view); - register_html_events_handlers (view, body); - - change_cid_images_src_to_base64 (view); - - if (view->priv->inline_spelling) - e_html_editor_view_force_spell_check (view); - else - e_html_editor_view_turn_spell_check_off (view); - - set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), view->priv->html_mode); - - dom_window = webkit_dom_document_get_default_view (document); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (dom_window), - "scroll", - G_CALLBACK (body_scroll_event_cb), - FALSE, - view); -} - -void -e_html_editor_view_clear_history (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *ev; - - if (view->priv->history != NULL) { - g_list_free_full (view->priv->history, (GDestroyNotify) free_history_event); - view->priv->history = NULL; - } - - view->priv->history_size = 0; - view->priv->dont_save_history_in_body_input = FALSE; - view->priv->undo_redo_in_progress = FALSE; - - ev = g_new0 (EHTMLEditorViewHistoryEvent, 1); - ev->type = HISTORY_START; - view->priv->history = g_list_append (view->priv->history, ev); - - view->priv->can_undo = FALSE; - g_object_notify (G_OBJECT (view), "can-undo"); - view->priv->can_redo = FALSE; - g_object_notify (G_OBJECT (view), "can-redo"); -} - -static gboolean -needs_conversion (WebKitDOMDocument *document) -{ - WebKitDOMElement *element; - - element = webkit_dom_document_query_selector ( - document, - "b, i , u, table, hr, tt, font, sub, sup, h1, h2, h3, h4, h5, h6, " - "address, img:not([data-inline])", - NULL); - - return element ? TRUE : FALSE; -} - -/** - * e_html_editor_view_set_html_mode: - * @view: an #EHTMLEditorView - * @html_mode: @TRUE to enable HTML mode, @FALSE to enable plain text mode - * - * When switching from HTML to plain text mode, user will be prompted whether - * he/she really wants to switch the mode and lose all formatting. When user - * declines, the property is not changed. When they accept, the all formatting - * is lost. - */ -void -e_html_editor_view_set_html_mode (EHTMLEditorView *view, - gboolean html_mode) -{ - EHTMLEditorSelection *selection; - gboolean is_new_message, converted, edit_as_new, message, convert; - gboolean reply, hide; - WebKitDOMHTMLElement *body; - WebKitDOMDocument *document; - - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (html_mode == view->priv->html_mode) - return; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - is_new_message = view->priv->is_new_message; - converted = webkit_dom_element_has_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-converted"); - edit_as_new = webkit_dom_element_has_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-edit-as-new"); - message = webkit_dom_element_has_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-message"); - - reply = !is_new_message && !edit_as_new && message; - hide = !reply && !converted; - - convert = message && ((!hide && reply && !converted) || (edit_as_new && !converted)); - convert = convert && !is_new_message; - - /* If toggling from HTML to plain text mode, ask user first */ - if (!html_mode && (needs_conversion (document) || - (convert && !view->priv->is_message_from_draft))) { - if (!show_lose_formatting_dialog (view)) - return; - - view->priv->html_mode = html_mode; - - convert_when_changing_composer_mode (view); - style_updated_cb (view); - e_html_editor_selection_scroll_to_caret (selection); - - e_html_editor_view_set_changed (view, TRUE); - - goto out; - } - - view->priv->html_mode = html_mode; - - /* Update fonts - in plain text we only want monospace */ - e_html_editor_view_update_fonts (view); - - if (view->priv->html_mode) { - process_content_to_html_changing_composer_mode (view); - g_object_notify (G_OBJECT (selection), "font-color"); - } else - process_content_to_plain_text_changing_composer_mode (view); - - style_updated_cb (view); - out: - set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), view->priv->html_mode); - - e_html_editor_view_clear_history (view); - - g_object_notify (G_OBJECT (view), "html-mode"); -} - -void -e_html_editor_view_save_history_for_drop (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *event; - gint ii, length; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMDOMWindow *dom_window; - WebKitDOMNodeList *list; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - /* When the image is DnD inside the view WebKit removes the wrapper that - * is used for resizing the image, so we have to recreate it again. */ - list = webkit_dom_document_query_selector_all (document, ":not(span) > img[data-inline]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMElement *element; - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - - element = webkit_dom_document_create_element (document, "span", NULL); - webkit_dom_element_set_class_name (element, "-x-evo-resizable-wrapper"); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - node, - NULL); - - webkit_dom_node_append_child (WEBKIT_DOM_NODE (element), node, NULL); - g_object_unref (node); - } - g_object_unref (list); - - /* When the image is moved the new selection is created after after it, so - * lets collapse the selection to have the caret right after the image. */ - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - - /* Remove the last inserted history event as this one was inserted in - * body_input_event_cb and is wrong as its type is HISTORY_INPUT. */ - /* FIXME we could probably disable the HTML input event callback while - * doing DnD within the view */ - if (((EHTMLEditorViewHistoryEvent *) (view->priv->history->data))->type == HISTORY_INPUT) - remove_history_event (view, view->priv->history); - - event = g_new0 (EHTMLEditorViewHistoryEvent, 1); - event->type = HISTORY_INSERT_HTML; - - /* Get the dropped content. It's easy as it is selected by WebKit. */ - fragment = webkit_dom_range_clone_contents (range, NULL); - event->data.string.from = NULL; - /* Get the HTML content of the dropped content. */ - event->data.string.to = get_node_inner_html (WEBKIT_DOM_NODE (fragment)); - - e_html_editor_selection_get_selection_coordinates ( - view->priv->selection, - &event->before.start.x, - &event->before.start.y, - &event->before.end.x, - &event->before.end.y); - - event->before.end.x = event->before.start.x; - event->before.end.y = event->before.start.y; - - if (length > 0) - webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL); - else - webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); - - e_html_editor_selection_get_selection_coordinates ( - view->priv->selection, - &event->after.start.x, - &event->after.start.y, - &event->after.end.x, - &event->after.end.y); - - e_html_editor_view_insert_new_history_event (view, event); - - if (!view->priv->html_mode) { - WebKitDOMNodeList *list; - gint ii, length; - - list = webkit_dom_document_query_selector_all ( - document, "span[style^=font-family]", NULL); - length = webkit_dom_node_list_get_length (list); - if (length > 0) - e_html_editor_selection_save (view->priv->selection); - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *span, *child; - - span = webkit_dom_node_list_item (list, ii); - while ((child = webkit_dom_node_get_first_child (span))) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (span), - child, - span, - NULL); - - remove_node (span); - g_object_unref (span); - } - g_object_unref (list); - - if (length > 0) - e_html_editor_selection_restore (view->priv->selection); - } - - e_html_editor_view_force_spell_check_in_viewport (view); - - g_object_unref (range); - g_object_unref (dom_selection); - g_object_unref (dom_window); -} - -static void -html_editor_view_drag_end_cb (EHTMLEditorView *view, - GdkDragContext *context) -{ - e_html_editor_view_save_history_for_drop (view); -} - -static void -html_editor_view_owner_change_clipboard_cb (GtkClipboard *clipboard, - GdkEventOwnerChange *event, - EHTMLEditorView *view) -{ - if (!E_IS_HTML_EDITOR_VIEW (view)) - return; - - if (view->priv->copy_action_triggered && event->owner) - view->priv->copy_paste_clipboard_in_view = TRUE; - else - view->priv->copy_paste_clipboard_in_view = FALSE; - - view->priv->copy_action_triggered = FALSE; -} - -static void -html_editor_view_owner_change_primary_cb (GtkClipboard *clipboard, - GdkEventOwnerChange *event, - EHTMLEditorView *view) -{ - if (!E_IS_HTML_EDITOR_VIEW (view)) - return; - - if (!event->owner || !view->priv->can_copy) - view->priv->copy_paste_primary_in_view = FALSE; -} - -static void -html_editor_view_copy_cut_clipboard_cb (EHTMLEditorView *view) -{ - view->priv->copy_action_triggered = TRUE; -} - -static void -html_editor_view_paste_clipboard_targets_cb (GtkClipboard *clipboard, - GdkAtom *targets, - gint n_targets, - EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - - if (targets == NULL || n_targets < 0) - return; - - selection = e_html_editor_view_get_selection (view); - - /* If view doesn't have focus, focus it */ - if (!gtk_widget_has_focus (GTK_WIDGET (view))) - gtk_widget_grab_focus (GTK_WIDGET (view)); - - /* Order is important here to ensure common use cases are - * handled correctly. See GNOME bug #603715 for details. */ - /* Prefer plain text over HTML when in the plain text mode, but only - * when pasting content from outside the editor view. */ - if (e_html_editor_view_get_html_mode (view) || - e_html_editor_view_is_pasting_content_from_itself (view)) { - gchar *content = NULL; - - if (e_targets_include_html (targets, n_targets)) { - if (!(content = e_clipboard_wait_for_html (clipboard))) - return; - - e_html_editor_selection_insert_html (selection, content); - g_free (content); - return; - } - - if (gtk_targets_include_text (targets, n_targets)) { - if (!(content = gtk_clipboard_wait_for_text (clipboard))) - return; - - e_html_editor_selection_insert_text (selection, content); - g_free (content); - return; - } - } else { - gchar *content = NULL; - - if (gtk_targets_include_text (targets, n_targets)) { - if (!(content = gtk_clipboard_wait_for_text (clipboard))) - return; - - e_html_editor_selection_insert_text (selection, content); - g_free (content); - return; - } - - if (e_targets_include_html (targets, n_targets)) { - if (!(content = e_clipboard_wait_for_html (clipboard))) - return; - - e_html_editor_selection_insert_html (selection, content); - g_free (content); - return; - } - } - - if (gtk_targets_include_image (targets, n_targets, TRUE)) { - gchar *uri; - - if (!(uri = e_util_save_image_from_clipboard (clipboard))) - return; - - e_html_editor_selection_insert_image (selection, uri); - - g_free (uri); - - return; - } -} - -static void -html_editor_view_paste_primary_clipboard_cb (EHTMLEditorView *view) -{ - GtkClipboard *clipboard; - - clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); - - gtk_clipboard_request_targets ( - clipboard, (GtkClipboardTargetsReceivedFunc) - html_editor_view_paste_clipboard_targets_cb, view); - - g_signal_stop_emission_by_name (view, "paste-primary-clipboard"); -} - -static void -html_editor_view_paste_clipboard_cb (EHTMLEditorView *view) -{ - GtkClipboard *clipboard; - - clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); - - gtk_clipboard_request_targets ( - clipboard, (GtkClipboardTargetsReceivedFunc) - html_editor_view_paste_clipboard_targets_cb, view); - - g_signal_stop_emission_by_name (view, "paste-clipboard"); -} - -void -e_html_editor_view_reconnect_paste_clipboard_signals (EHTMLEditorView *view) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - g_signal_handlers_disconnect_by_func ( - view, html_editor_view_paste_clipboard_cb, NULL); - g_signal_handlers_disconnect_by_func ( - view, html_editor_view_paste_primary_clipboard_cb, NULL); - - g_signal_connect ( - view, "paste-clipboard", - G_CALLBACK (html_editor_view_paste_clipboard_cb), NULL); - g_signal_connect ( - view, "paste-primary-clipboard", - G_CALLBACK (html_editor_view_paste_primary_clipboard_cb), NULL); -} - -static void -e_html_editor_view_init (EHTMLEditorView *view) -{ - WebKitWebSettings *settings; - GSettings *g_settings; - GSettingsSchema *settings_schema; - ESpellChecker *checker; - gchar **languages; - gchar *comma_separated; - const gchar *user_cache_dir; - - view->priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (view); - - webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), TRUE); - - /* Override the spell-checker, use our own */ - checker = e_spell_checker_new (); - webkit_set_text_checker (G_OBJECT (checker)); - g_object_unref (checker); - - /* Give spell check languages to WebKit */ - languages = e_spell_checker_list_active_languages (checker, NULL); - comma_separated = g_strjoinv (",", languages); - g_strfreev (languages); - - settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view)); - - g_object_set ( - G_OBJECT (settings), - "enable-developer-extras", TRUE, - "enable-dom-paste", TRUE, - "enable-file-access-from-file-uris", TRUE, - "enable-plugins", FALSE, - "enable-scripts", FALSE, - "enable-spell-checking", TRUE, - "respect-image-orientation", TRUE, - "spell-checking-languages", comma_separated, - NULL); - - g_free (comma_separated); - - webkit_web_view_set_settings (WEBKIT_WEB_VIEW (view), settings); - - view->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); - - /* Don't use CSS when possible to preserve compatibility with older - * versions of Evolution or other MUAs */ - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, "false"); - - g_signal_connect ( - view, "drag-end", - G_CALLBACK (html_editor_view_drag_end_cb), NULL); - g_signal_connect ( - view, "user-changed-contents", - G_CALLBACK (html_editor_view_user_changed_contents_cb), NULL); - g_signal_connect ( - view, "selection-changed", - G_CALLBACK (html_editor_view_selection_changed_cb), NULL); - g_signal_connect ( - view, "should-show-delete-interface-for-element", - G_CALLBACK (html_editor_view_should_show_delete_interface_for_element), NULL); - g_signal_connect ( - view, "resource-request-starting", - G_CALLBACK (html_editor_view_resource_requested), NULL); - g_signal_connect ( - view, "notify::load-status", - G_CALLBACK (html_editor_view_load_status_changed), NULL); - - g_signal_connect ( - view, "paste-clipboard", - G_CALLBACK (html_editor_view_paste_clipboard_cb), NULL); - g_signal_connect ( - view, "paste-primary-clipboard", - G_CALLBACK (html_editor_view_paste_primary_clipboard_cb), NULL); - - g_signal_connect ( - view, "style-updated", - G_CALLBACK (style_updated_cb), NULL); - - g_signal_connect ( - view, "state-flags-changed", - G_CALLBACK (style_updated_cb), NULL); - - view->priv->selection = g_object_new ( - E_TYPE_HTML_EDITOR_SELECTION, - "html-editor-view", view, - NULL); - - g_settings = e_util_ref_settings ("org.gnome.desktop.interface"); - g_signal_connect ( - g_settings, "changed::font-name", - G_CALLBACK (e_html_editor_settings_changed_cb), view); - g_signal_connect ( - g_settings, "changed::monospace-font-name", - G_CALLBACK (e_html_editor_settings_changed_cb), view); - view->priv->font_settings = g_settings; - - g_settings = e_util_ref_settings ("org.gnome.evolution.mail"); - view->priv->mail_settings = g_settings; - - /* This schema is optional. Use if available. */ - settings_schema = g_settings_schema_source_lookup ( - g_settings_schema_source_get_default (), - "org.gnome.settings-daemon.plugins.xsettings", FALSE); - if (settings_schema != NULL) { - g_settings = e_util_ref_settings ("org.gnome.settings-daemon.plugins.xsettings"); - g_signal_connect ( - g_settings, "changed::antialiasing", - G_CALLBACK (e_html_editor_settings_changed_cb), view); - view->priv->aliasing_settings = g_settings; - } - - view->priv->inline_images = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) g_free); - - view->priv->composition_in_progress = FALSE; - - g_signal_connect ( - view, "copy-clipboard", - G_CALLBACK (html_editor_view_copy_cut_clipboard_cb), NULL); - - g_signal_connect ( - view, "cut-clipboard", - G_CALLBACK (html_editor_view_copy_cut_clipboard_cb), NULL); - - view->priv->owner_change_primary_cb_id = g_signal_connect ( - gtk_clipboard_get (GDK_SELECTION_PRIMARY), "owner-change", - G_CALLBACK (html_editor_view_owner_change_primary_cb), view); - - view->priv->owner_change_clipboard_cb_id = g_signal_connect ( - gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), "owner-change", - G_CALLBACK (html_editor_view_owner_change_clipboard_cb), view); - - view->priv->history = NULL; - e_html_editor_view_clear_history (view); - - e_html_editor_view_update_fonts (view); - style_updated_cb (view); - - view->priv->body_input_event_removed = TRUE; - view->priv->is_editting_message = FALSE; - view->priv->is_message_from_draft = FALSE; - view->priv->is_message_from_selection = FALSE; - view->priv->is_message_from_edit_as_new = FALSE; - view->priv->is_new_message = FALSE; - view->priv->convert_in_situ = FALSE; - view->priv->return_key_pressed = FALSE; - view->priv->space_key_pressed = FALSE; - view->priv->smiley_written = FALSE; - view->priv->undo_redo_in_progress = FALSE; - view->priv->dont_save_history_in_body_input = FALSE; - view->priv->style_change_callbacks_blocked = FALSE; - view->priv->selection_changed_callbacks_blocked = FALSE; - view->priv->copy_paste_clipboard_in_view = FALSE; - view->priv->copy_paste_primary_in_view = FALSE; - view->priv->copy_action_triggered = FALSE; - view->priv->pasting_primary_clipboard = FALSE; - view->priv->renew_history_after_coordinates = TRUE; - - view->priv->spell_check_on_scroll_event_source_id = 0; - - /* Make WebKit think we are displaying a local file, so that it - * does not block loading resources from file:// protocol */ - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (view), "", "text/html", "UTF-8", "file://"); - - if (emd_global_http_cache == NULL) { - user_cache_dir = e_get_user_cache_dir (); - emd_global_http_cache = camel_data_cache_new (user_cache_dir, NULL); - - if (emd_global_http_cache) { - /* cache expiry - 2 hour access, 1 day max */ - camel_data_cache_set_expire_age ( - emd_global_http_cache, 24 * 60 * 60); - camel_data_cache_set_expire_access ( - emd_global_http_cache, 2 * 60 * 60); - } - } -} - -/** - * e_html_editor_view_get_inline_spelling: - * @view: an #EHTMLEditorView - * - * Returns whether automatic spellchecking is enabled or not. When enabled, - * editor will perform spellchecking as user is typing. Otherwise spellcheck - * has to be run manually from menu. - * - * Returns: @TRUE when automatic spellchecking is enabled, @FALSE otherwise. - */ -gboolean -e_html_editor_view_get_inline_spelling (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->inline_spelling; -} - -/** - * e_html_editor_view_set_inline_spelling: - * @view: an #EHTMLEditorView - * @inline_spelling: @TRUE to enable automatic spellchecking, @FALSE otherwise - * - * Enables or disables automatic spellchecking. - */ -void -e_html_editor_view_set_inline_spelling (EHTMLEditorView *view, - gboolean inline_spelling) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (view->priv->inline_spelling == inline_spelling) - return; - - view->priv->inline_spelling = inline_spelling; - - if (inline_spelling) - e_html_editor_view_force_spell_check (view); - else - e_html_editor_view_turn_spell_check_off (view); - - g_object_notify (G_OBJECT (view), "inline-spelling"); -} - -/** - * e_html_editor_view_get_magic_links: - * @view: an #EHTMLEditorView - * - * Returns whether automatic links conversion is enabled. When enabled, the editor - * will automatically convert any HTTP links into clickable HTML links. - * - * Returns: @TRUE when magic links are enabled, @FALSE otherwise. - */ -gboolean -e_html_editor_view_get_magic_links (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->magic_links; -} - -/** - * e_html_editor_view_set_magic_links: - * @view: an #EHTMLEditorView - * @magic_links: @TRUE to enable magic links, @FALSE to disable them - * - * Enables or disables automatic links conversion. - */ -void -e_html_editor_view_set_magic_links (EHTMLEditorView *view, - gboolean magic_links) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (view->priv->magic_links == magic_links) - return; - - view->priv->magic_links = magic_links; - - g_object_notify (G_OBJECT (view), "magic-links"); -} - -/** - * e_html_editor_view_get_magic_smileys: - * @view: an #EHTMLEditorView - * - * Returns whether automatic conversion of smileys is enabled or disabled. When - * enabled, the editor will automatically convert text smileys ( :-), ;-),...) - * into images or Unicode characters. - * - * Returns: @TRUE when magic smileys are enabled, @FALSE otherwise. - */ -gboolean -e_html_editor_view_get_magic_smileys (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->magic_smileys; -} - -/** - * e_html_editor_view_set_magic_smileys: - * @view: an #EHTMLEditorView - * @magic_smileys: @TRUE to enable magic smileys, @FALSE to disable them - * - * Enables or disables magic smileys. - */ -void -e_html_editor_view_set_magic_smileys (EHTMLEditorView *view, - gboolean magic_smileys) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (view->priv->magic_smileys == magic_smileys) - return; - - view->priv->magic_smileys = magic_smileys; - - g_object_notify (G_OBJECT (view), "magic-smileys"); -} - -/** - * e_html_editor_view_get_unicode_smileys: - * @view: an #EHTMLEditorView - * - * Returns whether to use Unicode characters for smileys. - * - * Returns: @TRUE when Unicode characters should be used, @FALSE otherwise. - * - * Since: 3.16 - */ -gboolean -e_html_editor_view_get_unicode_smileys (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->unicode_smileys; -} - -/** - * e_html_editor_view_set_unicode_smileys: - * @view: an #EHTMLEditorView - * @unicode_smileys: @TRUE to use Unicode characters, @FALSE to use images - * - * Enables or disables the usage of Unicode characters for smileys. - * - * Since: 3.16 - */ -void -e_html_editor_view_set_unicode_smileys (EHTMLEditorView *view, - gboolean unicode_smileys) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (view->priv->unicode_smileys == unicode_smileys) - return; - - view->priv->unicode_smileys = unicode_smileys; - - g_object_notify (G_OBJECT (view), "unicode-smileys"); -} - -/** - * e_html_editor_view_get_spell_checker: - * @view: an #EHTMLEditorView - * - * Returns an #ESpellChecker object that is used to perform spellchecking. - * - * Returns: An always-valid #ESpellChecker object - */ -ESpellChecker * -e_html_editor_view_get_spell_checker (EHTMLEditorView *view) -{ - return E_SPELL_CHECKER (webkit_get_text_checker ()); -} - -static CamelMimePart * -e_html_editor_view_add_inline_image_from_element (EHTMLEditorView *view, - WebKitDOMElement *element, - const gchar *attribute, - const gchar *uid_domain) -{ - CamelStream *stream; - CamelDataWrapper *wrapper; - CamelMimePart *part = NULL; - gsize decoded_size; - gssize size; - gchar *mime_type = NULL; - gchar *element_src, *cid, *name; - const gchar *base64_encoded_data; - guchar *base64_decoded_data = NULL; - - if (!WEBKIT_DOM_IS_ELEMENT (element)) { - return NULL; - } - - element_src = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (element), attribute); - - base64_encoded_data = strstr (element_src, ";base64,"); - if (!base64_encoded_data) - goto out; - - mime_type = g_strndup ( - element_src + 5, - base64_encoded_data - (strstr (element_src, "data:") + 5)); - - /* Move to actual data */ - base64_encoded_data += 8; - - base64_decoded_data = g_base64_decode (base64_encoded_data, &decoded_size); - - stream = camel_stream_mem_new (); - size = camel_stream_write ( - stream, (gchar *) base64_decoded_data, decoded_size, NULL, NULL); - - if (size == -1) - goto out; - - wrapper = camel_data_wrapper_new (); - camel_data_wrapper_construct_from_stream_sync ( - wrapper, stream, NULL, NULL); - g_object_unref (stream); - - camel_data_wrapper_set_mime_type (wrapper, mime_type); - - part = camel_mime_part_new (); - camel_medium_set_content (CAMEL_MEDIUM (part), wrapper); - g_object_unref (wrapper); - - cid = camel_header_msgid_generate (uid_domain); - camel_mime_part_set_content_id (part, cid); - g_free (cid); - name = webkit_dom_element_get_attribute (element, "data-name"); - camel_mime_part_set_disposition (part, "inline"); - camel_mime_part_set_filename (part, name); - g_free (name); - camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64); -out: - g_free (mime_type); - g_free (element_src); - g_free (base64_decoded_data); - - return part; -} - -static GList * -html_editor_view_get_parts_for_inline_images (EHTMLEditorView *view, - const gchar *uid_domain, - GHashTable **inline_images) -{ - GList *parts = NULL; - gint length, ii; - WebKitDOMDocument *document; - WebKitDOMNodeList *list; - - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL); - g_return_val_if_fail (inline_images != NULL, NULL); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - list = webkit_dom_document_query_selector_all (document, "img[data-inline]", NULL); - - length = webkit_dom_node_list_get_length (list); - if (length == 0) - goto background; - - *inline_images = g_hash_table_new_full ( - g_str_hash, g_str_equal, g_free, g_free); - - for (ii = 0; ii < length; ii++) { - const gchar *id; - gchar *cid; - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - gchar *src = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (node), "src"); - - if ((id = g_hash_table_lookup (*inline_images, src)) != NULL) { - cid = g_strdup_printf ("cid:%s", id); - g_free (src); - } else { - CamelMimePart *part; - - part = e_html_editor_view_add_inline_image_from_element ( - view, WEBKIT_DOM_ELEMENT (node), "src", uid_domain); - parts = g_list_append (parts, part); - - id = camel_mime_part_get_content_id (part); - cid = g_strdup_printf ("cid:%s", id); - - g_hash_table_insert (*inline_images, src, g_strdup (id)); - } - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (node), "src", cid, NULL); - g_free (cid); - g_object_unref (node); - } - g_object_unref (list); - - background: - list = webkit_dom_document_query_selector_all ( - document, "[data-inline][background]", NULL); - - length = webkit_dom_node_list_get_length (list); - if (length == 0) { - g_object_unref (list); - return parts; - } - if (!*inline_images) - *inline_images = g_hash_table_new_full ( - g_str_hash, g_str_equal, g_free, g_free); - - for (ii = 0; ii < length; ii++) { - CamelMimePart *part; - const gchar *id; - gchar *cid = NULL; - WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); - gchar *src = webkit_dom_element_get_attribute ( - WEBKIT_DOM_ELEMENT (node), "background"); - - if ((id = g_hash_table_lookup (*inline_images, src)) != NULL) { - cid = g_strdup_printf ("cid:%s", id); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (node), "background", cid, NULL); - g_free (src); - } else { - part = e_html_editor_view_add_inline_image_from_element ( - view, WEBKIT_DOM_ELEMENT (node), "background", uid_domain); - if (part) { - parts = g_list_append (parts, part); - id = camel_mime_part_get_content_id (part); - g_hash_table_insert (*inline_images, src, g_strdup (id)); - cid = g_strdup_printf ("cid:%s", id); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (node), "background", cid, NULL); - } else - g_free (src); - } - g_object_unref (node); - g_free (cid); - } - - g_object_unref (list); - - return parts; -} - -/** - * e_html_editor_view_add_inline_image_from_mime_part: - * @composer: a composer object - * @part: a CamelMimePart containing image data - * - * This adds the mime part @part to @composer as an inline image. - **/ -void -e_html_editor_view_add_inline_image_from_mime_part (EHTMLEditorView *view, - CamelMimePart *part) -{ - CamelDataWrapper *dw; - CamelStream *stream; - GByteArray *byte_array; - gchar *src, *base64_encoded, *mime_type, *cid_src; - const gchar *cid, *name; - - stream = camel_stream_mem_new (); - dw = camel_medium_get_content (CAMEL_MEDIUM (part)); - g_return_if_fail (dw); - - mime_type = camel_data_wrapper_get_mime_type (dw); - camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL); - camel_stream_close (stream, NULL, NULL); - - byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream)); - - if (!byte_array->data) - return; - - base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len); - - name = camel_mime_part_get_filename (part); - /* Insert file name before new src */ - src = g_strconcat (name, ";data:", mime_type, ";base64,", base64_encoded, NULL); - - cid = camel_mime_part_get_content_id (part); - if (!cid) { - camel_mime_part_set_content_id (part, NULL); - cid = camel_mime_part_get_content_id (part); - } - cid_src = g_strdup_printf ("cid:%s", cid); - - g_hash_table_insert (view->priv->inline_images, cid_src, src); - - g_free (base64_encoded); - g_free (mime_type); - g_object_unref (stream); -} - -static void -restore_images (gchar *key, - gchar *value, - EHTMLEditorView *view) -{ - gchar *selector; - gint length, ii; - WebKitDOMDocument *document; - WebKitDOMNodeList *list; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - selector = g_strconcat ("[data-inline][background=\"cid:", value, "\"]", NULL); - list = webkit_dom_document_query_selector_all (document, selector, NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMElement *element = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_list_item (list, ii)); - - webkit_dom_element_set_attribute (element, "background", key, NULL); - g_object_unref (element); - } - g_free (selector); - g_object_unref (list); - - selector = g_strconcat ("[data-inline][src=\"cid:", value, "\"]", NULL); - list = webkit_dom_document_query_selector_all (document, selector, NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMElement *element = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_list_item (list, ii)); - - webkit_dom_element_set_attribute (element, "src", key, NULL); - g_object_unref (element); - } - g_free (selector); - g_object_unref (list); -} - -static void -html_editor_view_restore_images (EHTMLEditorView *view, - GHashTable **inline_images) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - g_hash_table_foreach (*inline_images, (GHFunc) restore_images, view); - - /* Remove all hashed images as user can modify them. */ - g_hash_table_remove_all (*inline_images); - g_hash_table_destroy (*inline_images); -} - -/** - * e_html_editor_view_get_text_html: - * @view: an #EHTMLEditorView: - * - * Returns processed HTML content of the editor document (without elements attributes - * used in Evolution composer) - * - * Returns: A newly allocated string - */ -gchar * -e_html_editor_view_get_text_html (EHTMLEditorView *view, - const gchar *from_domain, - GList **inline_images) -{ - gchar *html = NULL; - GHashTable *inline_images_to_restore = NULL; - - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL); - - if (inline_images && from_domain) - *inline_images = html_editor_view_get_parts_for_inline_images ( - view, from_domain, &inline_images_to_restore); - - html = process_content_to_html_for_exporting (view); - - if (inline_images && from_domain && inline_images_to_restore) - html_editor_view_restore_images (view, &inline_images_to_restore); - - return html; -} - -gchar * -e_html_editor_view_get_text_html_for_drafts_with_images (EHTMLEditorView *view, - const gchar *from_domain, - GList **inline_images) -{ - EHTMLEditorSelection *selection; - gboolean selection_saved = FALSE; - gchar *html = NULL; - GHashTable *inline_images_to_restore = NULL; - WebKitDOMDocument *document; - - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL); - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (inline_images && from_domain) - *inline_images = html_editor_view_get_parts_for_inline_images ( - view, from_domain, &inline_images_to_restore); - - e_html_editor_view_embed_styles (view); - selection_saved = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker") != NULL; - if (!selection_saved) - e_html_editor_selection_save (selection); - - html = process_content_for_saving_as_draft (view, FALSE); - - if (inline_images && from_domain && inline_images_to_restore) - html_editor_view_restore_images (view, &inline_images_to_restore); - - e_html_editor_view_remove_embed_styles (view); - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_in_viewport (view); - - if (selection_saved) - e_html_editor_selection_save (selection); - - return html; -} - -/** - * e_html_editor_view_get_text_html_for_drafts: - * @view: an #EHTMLEditorView: - * - * Returns HTML content of the editor document (with elements attributes used in - * Evolution composer) - * - * Returns: A newly allocated string - */ -gchar * -e_html_editor_view_get_text_html_for_drafts (EHTMLEditorView *view) -{ - return process_content_for_saving_as_draft (view, FALSE); -} - -/** - * e_html_editor_view_get_text_html_for_drafts: - * @view: an #EHTMLEditorView: - * - * Returns HTML content of the editor document (with elements attributes used in - * Evolution composer) - * - * Returns: A newly allocated string - */ -gchar * -e_html_editor_view_get_body_text_html_for_drafts (EHTMLEditorView *view) -{ - return process_content_for_saving_as_draft (view, TRUE); -} - -/** - * e_html_editor_view_get_text_plain: - * @view: an #EHTMLEditorView - * - * Returns plain text content of the @view. The algorithm removes any - * formatting or styles from the document and keeps only the text and line - * breaks. - * - * Returns: A newly allocated string with plain text content of the document. - */ -gchar * -e_html_editor_view_get_text_plain (EHTMLEditorView *view) -{ - return process_content_to_plain_text_for_exporting (view); -} - -void -e_html_editor_view_convert_and_insert_plain_text (EHTMLEditorView *view, - const gchar *text) -{ - html_editor_view_insert_converted_html_into_selection (view, FALSE, text); -} - -void -e_html_editor_view_convert_and_insert_html_to_plain_text (EHTMLEditorView *view, - const gchar *html) -{ - html_editor_view_insert_converted_html_into_selection (view, TRUE, html); -} - -void -e_html_editor_view_convert_element_from_html_to_plain_text (EHTMLEditorView *view, - WebKitDOMElement *element) -{ - convert_element_from_html_to_plain_text (view, element, NULL, NULL); -} - -/** - * e_html_editor_view_set_text_html: - * @view: an #EHTMLEditorView - * @text: HTML code to load into the editor - * - * Loads given @text into the editor, destroying any content already present. - */ -void -e_html_editor_view_set_text_html (EHTMLEditorView *view, - const gchar *text) -{ - WebKitLoadStatus status; - - /* It can happen that the view is not ready yet (it is in the middle of - * another load operation) so we have to queue the current operation and - * redo it again when the view is ready. This was happening when loading - * the stuff in EMailSignatureEditor. */ - status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view)); - if (status != WEBKIT_LOAD_FINISHED) { - html_editor_view_queue_post_reload_operation ( - view, - (PostReloadOperationFunc) e_html_editor_view_set_text_html, - g_strdup (text), - g_free); - return; - } - - view->priv->reload_in_progress = TRUE; - - if (view->priv->is_message_from_draft) { - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://"); - return; - } - - if (view->priv->is_message_from_selection && !view->priv->html_mode) { - if (text && *text) - view->priv->convert_in_situ = TRUE; - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://"); - return; - } - - /* Only convert messages that are in HTML */ - if (!view->priv->html_mode) { - if (strstr (text, "<!-- text/html -->")) { - if (!show_lose_formatting_dialog (view)) { - e_html_editor_view_set_html_mode (view, TRUE); - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://"); - return; - } - } - if (text && *text) - view->priv->convert_in_situ = TRUE; - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://"); - } else - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://"); -} - -/** - * e_html_editor_view_set_text_plain: - * @view: an #EHTMLEditorView - * @text: A plain text to load into the editor - * - * Loads given @text into the editor, destryoing any content already present. - */ -void -e_html_editor_view_set_text_plain (EHTMLEditorView *view, - const gchar *text) -{ - WebKitLoadStatus status; - - /* It can happen that the view is not ready yet (it is in the middle of - * another load operation) so we have to queue the current operation and - * redo it again when the view is ready. This was happening when loading - * the stuff in EMailSignatureEditor. */ - status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view)); - if (status != WEBKIT_LOAD_FINISHED) { - html_editor_view_queue_post_reload_operation ( - view, - (PostReloadOperationFunc) e_html_editor_view_set_text_plain, - g_strdup (text), - g_free); - return; - } - - view->priv->reload_in_progress = TRUE; - - html_editor_convert_view_content (view, text); -} - -/** - * e_html_editor_view_paste_as_text: - * @view: an #EHTMLEditorView - * - * Pastes current content of clipboard into the editor without formatting - */ -void -e_html_editor_view_paste_as_text (EHTMLEditorView *view) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - html_editor_view_paste_as_text (view); -} - -/** - * e_html_editor_view_paste_clipboard_quoted: - * @view: an #EHTMLEditorView - * - * Pastes current content of clipboard into the editor as quoted text - */ -void -e_html_editor_view_paste_clipboard_quoted (EHTMLEditorView *view) -{ - EHTMLEditorViewClass *class; - - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - class = E_HTML_EDITOR_VIEW_GET_CLASS (view); - g_return_if_fail (class->paste_clipboard_quoted != NULL); - - class->paste_clipboard_quoted (view); -} - -/** - * e_html_editor_view_update_fonts: - * @view: an #EHTMLEditorView - * - * Forces the editor to reload font settings from WebKitWebSettings and apply - * it on the content of the editor document. - */ -void -e_html_editor_view_update_fonts (EHTMLEditorView *view) -{ - gboolean mark_citations, use_custom_font; - gchar *base64, *font, *aa = NULL, *citation_color; - const gchar *styles[] = { "normal", "oblique", "italic" }; - const gchar *smoothing = NULL; - GString *stylesheet; - PangoFontDescription *min_size, *ms, *vw; - WebKitWebSettings *settings; - - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - use_custom_font = g_settings_get_boolean ( - view->priv->mail_settings, "use-custom-font"); - - if (use_custom_font) { - font = g_settings_get_string ( - view->priv->mail_settings, "monospace-font"); - ms = pango_font_description_from_string (font ? font : "monospace 10"); - g_free (font); - } else { - font = g_settings_get_string ( - view->priv->font_settings, "monospace-font-name"); - ms = pango_font_description_from_string (font ? font : "monospace 10"); - g_free (font); - } - - if (view->priv->html_mode) { - if (use_custom_font) { - font = g_settings_get_string ( - view->priv->mail_settings, "variable-width-font"); - vw = pango_font_description_from_string (font ? font : "serif 10"); - g_free (font); - } else { - font = g_settings_get_string ( - view->priv->font_settings, "font-name"); - vw = pango_font_description_from_string (font ? font : "serif 10"); - g_free (font); - } - } else { - /* When in plain text mode, force monospace font */ - vw = pango_font_description_copy (ms); - } - - stylesheet = g_string_new (""); - g_string_append_printf ( - stylesheet, - "body {\n" - " font-family: '%s';\n" - " font-size: %dpt;\n" - " font-weight: %d;\n" - " font-style: %s;\n" - " -webkit-line-break: after-white-space;\n", - pango_font_description_get_family (vw), - pango_font_description_get_size (vw) / PANGO_SCALE, - pango_font_description_get_weight (vw), - styles[pango_font_description_get_style (vw)]); - - if (view->priv->aliasing_settings != NULL) - aa = g_settings_get_string ( - view->priv->aliasing_settings, "antialiasing"); - - if (g_strcmp0 (aa, "none") == 0) - smoothing = "none"; - else if (g_strcmp0 (aa, "grayscale") == 0) - smoothing = "antialiased"; - else if (g_strcmp0 (aa, "rgba") == 0) - smoothing = "subpixel-antialiased"; - - if (smoothing != NULL) - g_string_append_printf ( - stylesheet, - " -webkit-font-smoothing: %s;\n", - smoothing); - - g_free (aa); - - g_string_append (stylesheet, "}\n"); - - g_string_append_printf ( - stylesheet, - "pre,code,.pre {\n" - " font-family: '%s';\n" - " font-size: %dpt;\n" - " font-weight: %d;\n" - " font-style: %s;\n" - "}", - pango_font_description_get_family (ms), - pango_font_description_get_size (ms) / PANGO_SCALE, - pango_font_description_get_weight (ms), - styles[pango_font_description_get_style (ms)]); - - /* See bug #689777 for details */ - g_string_append ( - stylesheet, - "p,pre,code,address {\n" - " margin: 0;\n" - "}\n" - "h1,h2,h3,h4,h5,h6 {\n" - " margin-top: 0.2em;\n" - " margin-bottom: 0.2em;\n" - "}\n"); - - /* When inserting a table into contenteditable element the width of the - * cells is nearly zero and the td { min-height } doesn't work so put - * unicode zero width space before each cell. */ - g_string_append ( - stylesheet, - "td:before {\n" - " content: \"\xe2\x80\x8b\";" - "}\n"); - - g_string_append ( - stylesheet, - "img " - "{\n" - " height: inherit; \n" - " width: inherit; \n" - "}\n"); - - g_string_append ( - stylesheet, - "span.-x-evo-resizable-wrapper:hover " - "{\n" - " outline: 1px dashed red; \n" - " resize: both; \n" - " overflow: hidden; \n" - " display: inline-block; \n" - "}\n"); - - g_string_append ( - stylesheet, - "td:hover " - "{\n" - " outline: 1px dotted red;\n" - "}\n"); - - g_string_append_printf ( - stylesheet, - ".-x-evo-plaintext-table " - "{\n" - " border-collapse: collapse;\n" - " width: %dch;\n" - "}\n", - e_html_editor_selection_get_word_wrap_length (view->priv->selection)); - - g_string_append ( - stylesheet, - ".-x-evo-plaintext-table td" - "{\n" - " vertical-align: top;\n" - "}\n"); - - g_string_append ( - stylesheet, - "td > * " - "{\n" - " display : inline-block;\n" - "}\n"); - - g_string_append_printf ( - stylesheet, - "ul[data-evo-plain-text]" - "{\n" - " list-style: outside none;\n" - " -webkit-padding-start: %dch; \n" - "}\n", SPACES_PER_LIST_LEVEL); - - g_string_append_printf ( - stylesheet, - "ul[data-evo-plain-text] > li" - "{\n" - " list-style-position: outside;\n" - " text-indent: -%dch;\n" - "}\n", SPACES_PER_LIST_LEVEL - 1); - - g_string_append ( - stylesheet, - "ul[data-evo-plain-text] > li::before " - "{\n" - " content: \"*"UNICODE_NBSP"\";\n" - "}\n"); - - g_string_append_printf ( - stylesheet, - "ul[data-evo-plain-text].-x-evo-indented " - "{\n" - " -webkit-padding-start: %dch; \n" - "}\n", SPACES_PER_LIST_LEVEL); - - g_string_append ( - stylesheet, - "ul:not([data-evo-plain-text]) > li.-x-evo-align-center,ol > li.-x-evo-align-center" - "{\n" - " list-style-position: inside;\n" - "}\n"); - - g_string_append ( - stylesheet, - "ul:not([data-evo-plain-text]) > li.-x-evo-align-right, ol > li.-x-evo-align-right" - "{\n" - " list-style-position: inside;\n" - "}\n"); - - g_string_append_printf ( - stylesheet, - "ol" - "{\n" - " -webkit-padding-start: %dch; \n" - "}\n", SPACES_ORDERED_LIST_FIRST_LEVEL); - - g_string_append_printf ( - stylesheet, - "ol.-x-evo-indented" - "{\n" - " -webkit-padding-start: %dch; \n" - "}\n", SPACES_PER_LIST_LEVEL); - - g_string_append ( - stylesheet, - ".-x-evo-align-left " - "{\n" - " text-align: left; \n" - "}\n"); - - g_string_append ( - stylesheet, - ".-x-evo-align-center " - "{\n" - " text-align: center; \n" - "}\n"); - - g_string_append ( - stylesheet, - ".-x-evo-align-right " - "{\n" - " text-align: right; \n" - "}\n"); - - g_string_append ( - stylesheet, - "ol,ul " - "{\n" - " -webkit-margin-before: 0em; \n" - " -webkit-margin-after: 0em; \n" - "}\n"); - - g_string_append ( - stylesheet, - "blockquote " - "{\n" - " -webkit-margin-before: 0em; \n" - " -webkit-margin-after: 0em; \n" - "}\n"); - - g_string_append ( - stylesheet, - "a " - "{\n" - " word-wrap: break-word; \n" - " word-break: break-all; \n" - "}\n"); - - citation_color = g_settings_get_string ( - view->priv->mail_settings, "citation-color"); - mark_citations = g_settings_get_boolean ( - view->priv->mail_settings, "mark-citations"); - - g_string_append ( - stylesheet, - "blockquote[type=cite] " - "{\n" - " padding: 0.0ex 0ex;\n" - " margin: 0ex;\n" - " -webkit-margin-start: 0em; \n" - " -webkit-margin-end : 0em; \n"); - - if (mark_citations && citation_color) - g_string_append_printf ( - stylesheet, - " color: %s !important; \n", - citation_color); - - g_free (citation_color); - citation_color = NULL; - - g_string_append (stylesheet, "}\n"); - - g_string_append_printf ( - stylesheet, - ".-x-evo-quote-character " - "{\n" - " color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (1)); - - g_string_append_printf ( - stylesheet, - ".-x-evo-quote-character+" - ".-x-evo-quote-character" - "{\n" - " color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (2)); - - g_string_append_printf ( - stylesheet, - ".-x-evo-quote-character+" - ".-x-evo-quote-character+" - ".-x-evo-quote-character" - "{\n" - " color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (3)); - - g_string_append_printf ( - stylesheet, - ".-x-evo-quote-character+" - ".-x-evo-quote-character+" - ".-x-evo-quote-character+" - ".-x-evo-quote-character" - "{\n" - " color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (4)); - - g_string_append_printf ( - stylesheet, - ".-x-evo-quote-character+" - ".-x-evo-quote-character+" - ".-x-evo-quote-character+" - ".-x-evo-quote-character+" - ".-x-evo-quote-character" - "{\n" - " color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (5)); - - g_string_append ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " padding: 0ch 1ch 0ch 1ch;\n" - " margin: 0ch;\n" - " border-width: 0px 2px 0px 2px;\n" - " border-style: none solid none solid;\n" - " border-radius: 2px;\n" - "}\n"); - - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (1)); - - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (2)); - - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (3)); - - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (4)); - - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (5)); - - base64 = g_base64_encode ((guchar *) stylesheet->str, stylesheet->len); - g_string_free (stylesheet, TRUE); - - stylesheet = g_string_new ("data:text/css;charset=utf-8;base64,"); - g_string_append (stylesheet, base64); - g_free (base64); - - if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw) || !view->priv->html_mode) - min_size = ms; - else - min_size = vw; - - settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view)); - g_object_set ( - G_OBJECT (settings), - "default-font-size", pango_font_description_get_size (vw) / PANGO_SCALE, - "default-font-family", pango_font_description_get_family (vw), - "monospace-font-family", pango_font_description_get_family (ms), - "default-monospace-font-size", pango_font_description_get_size (ms) / PANGO_SCALE, - "minimum-font-size", pango_font_description_get_size (min_size) / PANGO_SCALE, - "user-stylesheet-uri", stylesheet->str, - NULL); - - g_string_free (stylesheet, TRUE); - - pango_font_description_free (ms); - pango_font_description_free (vw); -} - -/** - * e_html_editor_view_check_magic_links - * @view: an #EHTMLEditorView - * @include_space: If TRUE the pattern for link expects space on end - * - * Check if actual selection in given editor is link. If so, it is surrounded - * with ANCHOR element. - */ -void -e_html_editor_view_check_magic_links (EHTMLEditorView *view, - gboolean include_space) -{ - WebKitDOMRange *range; - - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - range = html_editor_view_get_dom_range (view); - html_editor_view_check_magic_links (view, range, include_space); - g_object_unref (range); -} - -void -e_html_editor_view_set_is_message_from_draft (EHTMLEditorView *view, - gboolean value) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - view->priv->is_message_from_draft = value; -} - -gboolean -e_html_editor_view_is_message_from_draft (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->is_message_from_draft; -} - -void -e_html_editor_view_set_is_message_from_selection (EHTMLEditorView *view, - gboolean value) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - view->priv->is_message_from_selection = value; -} - -gboolean -e_html_editor_view_is_message_from_edit_as_new (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->is_message_from_edit_as_new; -} -void -e_html_editor_view_set_is_message_from_edit_as_new (EHTMLEditorView *view, - gboolean value) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - view->priv->is_message_from_edit_as_new = value; -} - -gboolean -e_html_editor_view_content_is_new_message (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->is_new_message; -} - -void -e_html_editor_view_set_content_is_new_message (EHTMLEditorView *view, - gboolean value) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - view->priv->is_new_message = value; -} - -void -e_html_editor_view_set_is_editting_message (EHTMLEditorView *view, - gboolean value) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - view->priv->is_editting_message = value; -} - -void -e_html_editor_view_fix_file_uri_images (EHTMLEditorView *view) -{ - gint ii, length; - WebKitDOMNodeList *list; - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - list = webkit_dom_document_query_selector_all ( - document, "img[src^=\"file://\"]", NULL); - length = webkit_dom_node_list_get_length (list); - - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - gchar *uri; - - node = webkit_dom_node_list_item (list, ii); - uri = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "src"); - e_html_editor_selection_replace_image_src ( - view->priv->selection, WEBKIT_DOM_ELEMENT (node), uri); - g_free (uri); - } - - g_object_unref (list); -} - -gboolean -e_html_editor_view_is_undo_redo_in_progress (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - return view->priv->undo_redo_in_progress; -} - -void -e_html_editor_view_set_undo_redo_in_progress (EHTMLEditorView *view, - gboolean value) -{ - view->priv->undo_redo_in_progress = value; -} - -static void -remove_forward_redo_history_events_if_needed (EHTMLEditorView *view) -{ - GList *history = view->priv->history; - GList *item; - - if (!history || !history->prev) - return; - - item = history->prev; - while (item) { - GList *prev_item = item->prev; - - remove_history_event (view, item); - item = prev_item; - } -} - -void -e_html_editor_view_insert_new_history_event (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (view->priv->undo_redo_in_progress) - return; - - d (printf ("\nINSERTING EVENT:\n")); - d (print_history_event (event)); - - remove_forward_redo_history_events_if_needed (view); - - if (view->priv->history_size >= HISTORY_SIZE_LIMIT) { - remove_history_event (view, g_list_last (view->priv->history)->prev); - while (((EHTMLEditorViewHistoryEvent *) (g_list_last (view->priv->history)->prev))->type == HISTORY_AND) { - remove_history_event (view, g_list_last (view->priv->history)->prev); - remove_history_event (view, g_list_last (view->priv->history)->prev); - } - - } - - view->priv->history = g_list_prepend (view->priv->history, event); - view->priv->history_size++; - view->priv->can_undo = TRUE; - - d (print_history (view)); - - g_object_notify (G_OBJECT (view), "can-undo"); -} - -static WebKitDOMRange * -get_range_for_point (WebKitDOMDocument *document, - EHTMLEditorViewSelectionPoint point) -{ - glong scroll_left, scroll_top; - WebKitDOMHTMLElement *body; - WebKitDOMRange *range; - - body = webkit_dom_document_get_body (document); - scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body)); - scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body)); - - range = webkit_dom_document_caret_range_from_point ( - document, point.x - scroll_left, point.y - scroll_top); - - /* The point is outside the viewport, scroll to it. */ - if (!range) { - WebKitDOMDOMWindow *dom_window; - - dom_window = webkit_dom_document_get_default_view (document); - webkit_dom_dom_window_scroll_to (dom_window, point.x, point.y); - - scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body)); - scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body)); - range = webkit_dom_document_caret_range_from_point ( - document, point.x - scroll_left, point.y - scroll_top); - g_object_unref (dom_window); - } - - return range; -} - -static void -restore_selection_to_history_event_state (EHTMLEditorView *view, - EHTMLEditorViewSelection selection_state) -{ - EHTMLEditorSelection *selection; - gboolean was_collapsed = FALSE; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMElement *element, *tmp; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - /* Restore the selection how it was before the event occured. */ - selection = e_html_editor_view_get_selection (view); - range = get_range_for_point (document, selection_state.start); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - - was_collapsed = selection_state.start.x == selection_state.end.x; - was_collapsed = was_collapsed && selection_state.start.y == selection_state.end.y; - if (was_collapsed) { - g_object_unref (dom_selection); - return; - } - - e_html_editor_selection_save (selection); - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - remove_node (WEBKIT_DOM_NODE (element)); - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - webkit_dom_element_remove_attribute (element, "id"); - - range = get_range_for_point (document, selection_state.end); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - - e_html_editor_selection_save (selection); - - tmp = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - remove_node (WEBKIT_DOM_NODE (tmp)); - - webkit_dom_element_set_id ( - element, "-x-evo-selection-start-marker"); - - e_html_editor_selection_restore (selection); - - g_object_unref (dom_selection); -} - -static gboolean -event_selection_was_collapsed (EHTMLEditorViewHistoryEvent *ev) -{ - return (ev->before.start.x == ev->before.end.x) && (ev->before.start.y == ev->before.end.y); -} - -static void -undo_delete (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event) -{ - EHTMLEditorSelection *selection; - gboolean empty, single_block; - gchar *content; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - WebKitDOMElement *element; - WebKitDOMNode *first_child, *fragment; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - fragment = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment), TRUE); - first_child = webkit_dom_node_get_first_child (fragment); - - content = webkit_dom_node_get_text_content (fragment); - empty = content && !*content; - g_free (content); - - /* Tabulator */ - single_block = event->type == HISTORY_INPUT; - single_block = single_block && event->before.start.x != 0 && event->before.end.y != 0; - - if (!single_block) { - /* One block delete */ - if ((single_block = WEBKIT_DOM_IS_ELEMENT (first_child))) - single_block = element_has_id (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-selection-start-marker"); - else - single_block = WEBKIT_DOM_IS_TEXT (first_child); - } - - /* Delete or BackSpace pressed in the beginning of a block or on its end. */ - if (event->type == HISTORY_DELETE && !single_block && - g_object_get_data (G_OBJECT (event->data.fragment), "history-concatenating-blocks")) { - WebKitDOMNode *node, *block; - - range = get_range_for_point (document, event->after.start); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - - node = webkit_dom_range_get_end_container (range, NULL); - block = e_html_editor_get_parent_block_node_from_child (node); - - if (webkit_dom_document_fragment_query_selector (event->data.fragment, ".-x-evo-quoted", NULL)) { - while ((node = webkit_dom_node_get_first_child (fragment))) { - if (WEBKIT_DOM_IS_ELEMENT (node) && - webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (node), ".-x-evo-quoted", NULL)) - - if (get_citation_level (block, FALSE) > 0) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - node, - block, - NULL); - } else { - WebKitDOMNode *next_block; - - next_block = webkit_dom_node_get_next_sibling (block); - while (next_block && is_citation_node (next_block)) - next_block = webkit_dom_node_get_first_child (next_block); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (next_block), - node, - next_block, - NULL); - } - else { - if (get_citation_level (block, FALSE) > 0) { - WebKitDOMNode *next_node; - - if ((next_node = split_node_into_two (block, -1))) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (next_node), - node, - next_node, - NULL); - else - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - node, - block, - NULL); - } else - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - node, - block, - NULL); - } - } - } else { - while ((node = webkit_dom_node_get_first_child (fragment))) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - node, - block, - NULL); - } - } - - remove_node (block); - - restore_selection_to_history_event_state (view, event->before); - - e_html_editor_view_force_spell_check_in_viewport (view); - - return; - } - - /* Redoing Return key press */ - if (event->type == HISTORY_INPUT && (empty || - g_object_get_data (G_OBJECT (event->data.fragment), "history-return-key"))) { - if (key_press_event_process_return_key (view)) { - body_key_up_event_process_return_key (view); - } else { - WebKitDOMElement *element; - WebKitDOMNode *next_sibling; - - range = get_range_for_point (document, event->before.start); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (dom_selection); - g_object_unref (range); - - e_html_editor_selection_save (selection); - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - if (next_sibling && !(WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling))) { - split_node_into_two (WEBKIT_DOM_NODE (element), 1); - } else { - WebKitDOMNode *block; - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (element)); - remove_selection_markers (document); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (block), - fragment, - webkit_dom_node_get_next_sibling (block), - NULL); - } - e_html_editor_selection_restore (selection); - } - - view->priv->return_key_pressed = TRUE; - range = html_editor_view_get_dom_range (view); - html_editor_view_check_magic_links (view, range, FALSE); - view->priv->return_key_pressed = FALSE; - g_object_unref (range); - e_html_editor_view_force_spell_check_in_viewport (view); - - return; - } - - if (!single_block) { - if (WEBKIT_DOM_IS_ELEMENT (first_child) && - !(WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (first_child) || - WEBKIT_DOM_IS_HTML_PRE_ELEMENT (first_child) || - (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (first_child) && - element_has_class (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-paragraph")))) - single_block = TRUE; - } - - /* Multi block delete */ - if (WEBKIT_DOM_IS_ELEMENT (first_child) && !single_block) { - gboolean delete; - WebKitDOMElement *signature; - WebKitDOMNode *node, *current_block, *last_child; - WebKitDOMNode *next_block, *insert_before; - - range = get_range_for_point (document, event->after.start); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - e_html_editor_selection_save (selection); - - if ((element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker"))) { - WebKitDOMNode *next_sibling; - - if ((next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element))) && - WEBKIT_DOM_IS_CHARACTER_DATA (next_sibling) && - webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (next_sibling)) == 1) { - gchar *data; - - data = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (next_sibling)); - if (data && *data == ' ') { - WebKitDOMElement *hidden_space; - - hidden_space = webkit_dom_document_create_element (document, "span", NULL); - webkit_dom_element_set_attribute ( - hidden_space, "data-hidden-space", "", NULL); - remove_node (next_sibling); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - WEBKIT_DOM_NODE (hidden_space), - webkit_dom_node_get_previous_sibling ( - WEBKIT_DOM_NODE (element)), - NULL); - } - g_free (data); - } - } - - element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"); - /* Get the last block in deleted content. */ - last_child = webkit_dom_node_get_last_child (fragment); - while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child)) - last_child = webkit_dom_node_get_last_child (last_child); - - /* All the nodes that are in current block after the caret position - * belongs on the end of the deleted content. */ - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - - /* FIXME Ugly hack */ - /* If the selection ended in signature, the structure will be broken - * thus we saved the whole signature into deleted fragment and we will - * restore the whole signature, but we need to remove the rest of the - * signature that's left after delete to avoid duplications. */ - signature = webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL); - delete = signature && webkit_dom_node_contains (WEBKIT_DOM_NODE (signature), WEBKIT_DOM_NODE (element)); - if (!delete) { - WebKitDOMNode *tmp_node; - - tmp_node = webkit_dom_node_get_last_child (fragment); - delete = tmp_node && WEBKIT_DOM_IS_ELEMENT (tmp_node) && - element_has_class (WEBKIT_DOM_ELEMENT (tmp_node), "-x-evo-signature-wrapper"); - } - - current_block = e_html_editor_get_parent_block_node_from_child (WEBKIT_DOM_NODE (element)); - - while (node) { - WebKitDOMNode *next_sibling, *parent_node; - - next_sibling = webkit_dom_node_get_next_sibling (node); - parent_node = webkit_dom_node_get_parent_node (node); - /* Check if the whole element was deleted. If so replace it. */ - if (!next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (node) && - !webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element))) { - WebKitDOMNode *tmp_node; - WebKitDOMElement *tmp_element; - - tmp_node = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (tmp_node), - fragment, - tmp_node, - NULL); - - /* Remove empty blockquotes, if presented. */ - tmp_element = webkit_dom_document_query_selector ( - document, "blockquote[type=cite]:empty", NULL); - if (tmp_element) - remove_node (WEBKIT_DOM_NODE (tmp_element)); - - merge_siblings_if_necessary (document, event->data.fragment); - - tmp_node = webkit_dom_node_get_last_child (last_child); - if (tmp_node && WEBKIT_DOM_IS_ELEMENT (tmp_node) && - element_has_class (WEBKIT_DOM_ELEMENT (tmp_node), "-x-evo-quoted")) { - webkit_dom_node_append_child ( - last_child, - WEBKIT_DOM_NODE ( - webkit_dom_document_create_element ( - document, "br", NULL)), - NULL); - } - - remove_selection_markers (document); - - restore_selection_to_history_event_state (view, event->before); - - e_html_editor_view_force_spell_check_in_viewport (view); - - g_object_unref (dom_selection); - - return; - } else if (!next_sibling && !webkit_dom_node_is_same_node (parent_node, current_block)) - next_sibling = webkit_dom_node_get_next_sibling (parent_node); - - if (delete) - remove_node (node); - else - webkit_dom_node_append_child (last_child, node, NULL); - node = next_sibling; - } - - /* Get the first block in deleted content. */ - while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (first_child)) - first_child = webkit_dom_node_get_first_child (first_child); - - /* All the nodes that are in the first block of the deleted content - * belongs to the current block right after the caret position. */ - while ((node = webkit_dom_node_get_first_child (first_child))) - webkit_dom_node_append_child (current_block, node, NULL); - - next_block = webkit_dom_node_get_next_sibling (current_block); - insert_before = next_block; - - if (!insert_before) { - WebKitDOMNode *parent = NULL; - - parent = current_block; - while ((parent = webkit_dom_node_get_parent_node (parent)) && - !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - insert_before = webkit_dom_node_get_next_sibling (parent); - if (insert_before) - break; - } - } - - /* Split a BLOCKQUOTE if the deleted content started with BLOCKQUOTE */ - if (insert_before && - WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT ( - webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment))) && - get_citation_level (insert_before, FALSE > 0)) - insert_before = split_node_into_two (insert_before, -1); - - /* Remove the first block from deleted content as its content was already - * moved to the right place. */ - remove_node (first_child); - - /* Insert the deleted content. */ - if (insert_before) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (insert_before), - WEBKIT_DOM_NODE (fragment), - insert_before, - NULL); - else - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE ( - webkit_dom_document_get_body (document)), - WEBKIT_DOM_NODE (fragment), - NULL); - - wrap_and_quote_element (view, WEBKIT_DOM_ELEMENT (current_block)); - - if (WEBKIT_DOM_IS_ELEMENT (last_child)) - wrap_and_quote_element (view, WEBKIT_DOM_ELEMENT (last_child)); - - merge_siblings_if_necessary (document, event->data.fragment); - - /* If undoing drag and drop where the whole line was moved we need - * to correct the selection. */ - if (g_object_get_data (G_OBJECT (event->data.fragment), "history-drag-and-drop") && - (element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker"))) { - WebKitDOMNode *prev_block; - - prev_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - if ((prev_block = webkit_dom_node_get_previous_sibling (prev_block))) - webkit_dom_node_append_child ( - prev_block, WEBKIT_DOM_NODE (element), NULL); - } - - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_in_viewport (view); - } else { - gboolean empty_text = FALSE, was_link = FALSE; - WebKitDOMNode *prev_sibling, *next_sibling, *nd; - WebKitDOMNode *parent; - - element = webkit_dom_document_create_element (document, "span", NULL); - - /* Create temporary node on the selection where the delete occurred. */ - if (webkit_dom_document_fragment_query_selector (event->data.fragment, ".Apple-tab-span", NULL)) - range = get_range_for_point (document, event->before.start); - else - range = get_range_for_point (document, event->after.start); - - webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - - nd = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - if (nd && WEBKIT_DOM_IS_TEXT (nd)) { - gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (nd)); - glong length = webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (nd)); - - /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE - * character as when we will remove it paragraph will collapse. */ - if (length > 1) { - if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE)) - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (nd), 0, 1, "", NULL); - else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE)) - webkit_dom_character_data_replace_data ( - WEBKIT_DOM_CHARACTER_DATA (nd), length - 1, 1, "", NULL); - } else if (length == 0) - empty_text = TRUE; - - g_free (text); - } - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); - if (!nd || empty_text) { - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - WEBKIT_DOM_NODE (element), - parent, - NULL); - } - - /* Insert the deleted content back to the body. */ - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) { - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (first_child)) { - WebKitDOMNode *child; - - while ((child = webkit_dom_node_get_first_child (first_child))) - webkit_dom_node_append_child (parent, child, NULL); - - remove_node (first_child); - - was_link = TRUE; - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - fragment, - webkit_dom_node_get_next_sibling (parent), - NULL); - } else { - if (g_object_get_data (G_OBJECT (event->data.fragment), "history-removing-from-anchor") || - !event_selection_was_collapsed (event)) { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - fragment, - WEBKIT_DOM_NODE (element), - NULL); - } else { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - fragment, - webkit_dom_node_get_next_sibling (parent), - NULL); - } - } - } else { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - fragment, - WEBKIT_DOM_NODE (element), - NULL); - } - - webkit_dom_node_normalize (parent); - prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - if (prev_sibling && next_sibling) { - WebKitDOMNode *clone_prev, *clone_next; - - clone_prev = webkit_dom_node_clone_node (prev_sibling, FALSE); - clone_next = webkit_dom_node_clone_node (next_sibling, FALSE); - - if (webkit_dom_node_is_equal_node (clone_prev, clone_next)) { - WebKitDOMNode *child; - - while ((child = webkit_dom_node_get_first_child (next_sibling))) - webkit_dom_node_append_child (prev_sibling, child, NULL); - - remove_node (next_sibling); - } - } - - remove_node (WEBKIT_DOM_NODE (element)); - - if (event->type == HISTORY_DELETE && !view->priv->html_mode) { - WebKitDOMNode *current_block; - - current_block = e_html_editor_get_parent_block_node_from_child (parent); - if (get_citation_level (current_block, FALSE) > 0) - wrap_and_quote_element (view, WEBKIT_DOM_ELEMENT (current_block)); - } - - /* If the selection markers are presented restore the selection, - * otherwise the selection was not collapsed so select the deleted - * content as it was before the delete occurred. */ - if (webkit_dom_document_fragment_query_selector (event->data.fragment, "span#-x-evo-selection-start-marker", NULL)) - e_html_editor_selection_restore (selection); - else - restore_selection_to_history_event_state (view, event->before); - - if (event->type != HISTORY_INPUT) { - g_object_unref (range); - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - html_editor_view_check_magic_smileys (view, range); - if (!was_link) - html_editor_view_check_magic_links (view, range, FALSE); - } - - g_object_unref (range); - e_html_editor_view_force_spell_check_for_current_paragraph (view); - } - - g_object_unref (dom_selection); -} - -static void -redo_delete (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event) -{ - gboolean delete_key, control_key; - glong length = 1; - gint ii; - WebKitDOMDocument *document; - WebKitDOMDocumentFragment *fragment = event->data.fragment; - WebKitDOMHTMLElement *body; - WebKitDOMNode *node; - - restore_selection_to_history_event_state (view, event->before); - - delete_key = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), "history-delete-key")); - control_key = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), "history-control-key")); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - if (!view->priv->html_mode) - set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), TRUE); - - if (!delete_key && key_press_event_process_backspace_key (view)) - goto out; - - if (key_press_event_process_delete_or_backspace_key (view, delete_key, NULL)) - goto out; - - if (control_key) { - gchar *text_content; - - text_content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (fragment)); - length = g_utf8_strlen (text_content, -1); - control_key = length > 1; - - g_free (text_content); - } - - /* If concatenating two blocks with pressing Delete on the end - * of the previous one and the next node contain content that - * is wrapped on multiple lines, the last line will by separated - * by WebKit to the separate block. To avoid it let's remove - * all quoting and wrapping from the next paragraph. */ - if (delete_key && - GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), "history-concatenating-blocks"))) { - WebKitDOMNode *current_block, *next_block, *node; - WebKitDOMRange *range; - - range = html_editor_view_get_dom_range (view); - node = webkit_dom_range_get_end_container (range, NULL); - g_object_unref (range); - current_block = e_html_editor_get_parent_block_node_from_child (node); - if (get_citation_level (current_block, FALSE) > 0 && - (next_block = webkit_dom_node_get_next_sibling (current_block))) { - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (next_block)); - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (next_block)); - } - } - - for (ii = 0; ii < length; ii++) { - e_html_editor_view_exec_command ( - view, - delete_key ? E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE : - E_HTML_EDITOR_VIEW_COMMAND_DELETE, - NULL); - } - - /* Really don't know why, but when the selection marker nodes were in - * anchors then we need to do an extra delete command otherwise we will - * end with two blocks split in half. */ - node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); - while ((node = webkit_dom_node_get_first_child (node))) { - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) { - e_html_editor_view_exec_command ( - view, - E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE, - NULL); - break; - } - } - - node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)); - while ((node = webkit_dom_node_get_last_child (node))) { - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) { - e_html_editor_view_exec_command ( - view, - E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE, - NULL); - break; - } - } - - out: - view->priv->dont_save_history_in_body_input = TRUE; - view->priv->undo_redo_in_progress = FALSE; - body_input_event_cb (NULL, NULL, view); - view->priv->dont_save_history_in_body_input = FALSE; - view->priv->undo_redo_in_progress = TRUE; - view->priv->renew_history_after_coordinates = FALSE; - body_key_up_event_process_backspace_or_delete (view, delete_key); - view->priv->renew_history_after_coordinates = TRUE; - - if (!view->priv->html_mode) - set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), FALSE); - - restore_selection_to_history_event_state (view, event->after); - - e_html_editor_view_force_spell_check_for_current_paragraph (view); -} - -typedef void (*SelectionStyleChangeFunc) (EHTMLEditorSelection *selection, gint style); - -static void -undo_redo_style_change (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - SelectionStyleChangeFunc func; - - selection = e_html_editor_view_get_selection (view); - - switch (event->type) { - case HISTORY_ALIGNMENT: - func = (SelectionStyleChangeFunc) e_html_editor_selection_set_alignment; - break; - case HISTORY_BOLD: - func = e_html_editor_selection_set_bold; - break; - case HISTORY_BLOCK_FORMAT: - func = (SelectionStyleChangeFunc) e_html_editor_selection_set_block_format; - break; - case HISTORY_FONT_SIZE: - func = (SelectionStyleChangeFunc) e_html_editor_selection_set_font_size; - break; - case HISTORY_ITALIC: - func = e_html_editor_selection_set_italic; - break; - case HISTORY_MONOSPACE: - func = e_html_editor_selection_set_monospaced; - break; - case HISTORY_STRIKETHROUGH: - func = e_html_editor_selection_set_strikethrough; - break; - case HISTORY_UNDERLINE: - func = e_html_editor_selection_set_underline; - break; - default: - return; - } - - restore_selection_to_history_event_state (view, undo ? event->after : event->before); - - func (selection, undo ? event->data.style.from : event->data.style.to); - - restore_selection_to_history_event_state (view, undo ? event->before : event->after); -} - -static void -undo_redo_indent (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - gboolean was_indent = FALSE; - EHTMLEditorSelection *selection; - - selection = e_html_editor_view_get_selection (view); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - was_indent = event->data.style.from && event->data.style.to; - - if (undo) { - if (was_indent) - e_html_editor_selection_unindent (selection); - else - e_html_editor_selection_indent (selection); - } else { - if (was_indent) - e_html_editor_selection_indent (selection); - else - e_html_editor_selection_unindent (selection); - } - - if (undo) - restore_selection_to_history_event_state (view, event->before); -} - -static void -undo_redo_font_color (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - if (undo) - restore_selection_to_history_event_state (view, event->after); - - e_html_editor_view_exec_command ( - view, - E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR, - undo ? event->data.string.from : event->data.string.to); - - if (undo) - restore_selection_to_history_event_state (view, event->before); -} - -static void -undo_redo_wrap (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - - selection = e_html_editor_view_get_selection (view); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - if (undo) { - WebKitDOMNode *node; - WebKitDOMElement *element; - WebKitDOMRange *range; - - range = html_editor_view_get_dom_range (view); - node = webkit_dom_range_get_common_ancestor_container (range, NULL); - g_object_unref (range); - element = get_parent_block_element (WEBKIT_DOM_NODE (node)); - webkit_dom_element_remove_attribute (element, "data-user-wrapped"); - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (element)); - - e_html_editor_view_force_spell_check_for_current_paragraph (view); - } else - e_html_editor_selection_wrap_lines (selection); - - if (undo) - restore_selection_to_history_event_state (view, event->before); -} - -static void -undo_redo_page_dialog (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - WebKitDOMNamedNodeMap *attributes, *attributes_history; - gint length, length_history, ii, jj; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - if (undo) { - attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); - attributes_history = webkit_dom_element_get_attributes ( - WEBKIT_DOM_ELEMENT (event->data.dom.from)); - } else { - attributes_history = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); - attributes = webkit_dom_element_get_attributes ( - WEBKIT_DOM_ELEMENT (event->data.dom.to)); - } - - length = webkit_dom_named_node_map_get_length (attributes); - length_history = webkit_dom_named_node_map_get_length (attributes_history); - for (ii = length - 1; ii >= 0; ii--) { - gchar *name; - WebKitDOMNode *attr; - gboolean replaced = FALSE; - - attr = webkit_dom_named_node_map_item (attributes, ii); - name = webkit_dom_node_get_local_name (attr); - - for (jj = length_history - 1; jj >= 0; jj--) { - gchar *name_history; - WebKitDOMNode *attr_history; - - attr_history = webkit_dom_named_node_map_item (attributes_history, jj); - name_history = webkit_dom_node_get_local_name (attr_history); - if (g_strcmp0 (name, name_history) == 0) { - WebKitDOMNode *attr_clone; - - attr_clone = webkit_dom_node_clone_node ( - undo ? attr_history : attr, TRUE); - webkit_dom_element_set_attribute_node ( - WEBKIT_DOM_ELEMENT (body), - WEBKIT_DOM_ATTR (attr_clone), - NULL); - - /* Link color has to replaced in HEAD as well. */ - if (g_strcmp0 (name, "link") == 0) { - gchar *value; - GdkRGBA rgba; - - value = webkit_dom_node_get_node_value (attr_clone); - if (gdk_rgba_parse (&rgba, value)) - e_html_editor_view_set_link_color (view, &rgba); - g_free (value); - } - if (g_strcmp0 (name, "vlink") == 0) { - gchar *value; - GdkRGBA rgba; - - value = webkit_dom_node_get_node_value (attr_clone); - if (gdk_rgba_parse (&rgba, value)) - e_html_editor_view_set_visited_link_color (view, &rgba); - g_free (value); - } - replaced = TRUE; - } - g_free (name_history); - g_object_unref (attr_history); - if (replaced) - break; - } - - if (!replaced) { - if (undo) { - webkit_dom_element_remove_attribute_node ( - WEBKIT_DOM_ELEMENT (body), - WEBKIT_DOM_ATTR (attr), - NULL); - } else { - webkit_dom_element_set_attribute_node ( - WEBKIT_DOM_ELEMENT (body), - WEBKIT_DOM_ATTR ( - webkit_dom_node_clone_node (attr, TRUE)), - NULL); - } - } - g_free (name); - g_object_unref (attr); - } - g_object_unref (attributes); - g_object_unref (attributes_history); - - if (undo) - restore_selection_to_history_event_state (view, event->before); -} - -static void -undo_redo_hrule_dialog (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *element; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - e_html_editor_selection_save (selection); - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - if (undo) { - WebKitDOMNode *node; - WebKitDOMElement *parent; - - parent = get_parent_block_element (WEBKIT_DOM_NODE (element)); - if (event->data.dom.from) - node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent)); - else - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)); - - if (node && WEBKIT_DOM_IS_HTMLHR_ELEMENT (node)) { - if (!event->data.dom.from) - remove_node (node); - else - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (node), - webkit_dom_node_clone_node (event->data.dom.from, TRUE), - node, - NULL); - } - } else { - WebKitDOMNode *node; - WebKitDOMElement *parent; - - parent = get_parent_block_element (WEBKIT_DOM_NODE (element)); - - if (event->data.dom.from) { - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)); - - if (node && WEBKIT_DOM_IS_HTMLHR_ELEMENT (node)) - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (node), - webkit_dom_node_clone_node (event->data.dom.to, TRUE), - node, - NULL); - } else { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)), - webkit_dom_node_clone_node (event->data.dom.to, TRUE), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)), - NULL); - } - } - - if (undo) - restore_selection_to_history_event_state (view, event->before); - else - e_html_editor_selection_restore (selection); -} - -static void -undo_redo_image_dialog (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *element; - WebKitDOMNode *sibling, *image = NULL; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - e_html_editor_selection_save (selection); - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); - if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) { - if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling)) - image = sibling; - else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper")) - image = webkit_dom_node_get_first_child (sibling); - } - - if (!image) { - element = WEBKIT_DOM_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element))); - sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) { - if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling)) - image = sibling; - else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper")) - image = webkit_dom_node_get_first_child (sibling); - } - } - - if (!image) - return; - - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (image), - webkit_dom_node_clone_node (undo ? event->data.dom.from : event->data.dom.to, TRUE), - image, - NULL); - - if (undo) - restore_selection_to_history_event_state (view, event->before); - else - e_html_editor_selection_restore (selection); -} - -static void -undo_redo_link_dialog (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *anchor, *element; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - else - restore_selection_to_history_event_state (view, event->before); - - e_html_editor_selection_save (selection); - - element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"); - if (!element) - return; - - anchor = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (element), "A"); - if (undo) { - if (anchor) { - if (!event->data.dom.from) - remove_node (WEBKIT_DOM_NODE (anchor)); - else - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (anchor)), - webkit_dom_node_clone_node (event->data.dom.from, TRUE), - WEBKIT_DOM_NODE (anchor), - NULL); - } - } else { - if (!event->data.dom.to) { - if (anchor) - remove_node (WEBKIT_DOM_NODE (anchor)); - } else { - if (WEBKIT_DOM_IS_ELEMENT (event->data.dom.from) && anchor) { - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (anchor)), - webkit_dom_node_clone_node (event->data.dom.to, TRUE), - WEBKIT_DOM_NODE (anchor), - NULL); - } else { - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - webkit_dom_node_clone_node (event->data.dom.to, TRUE), - WEBKIT_DOM_NODE (element), - NULL); - - if (event->data.dom.from) - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL); - } - } - } - - if (undo) - restore_selection_to_history_event_state (view, event->before); - else - e_html_editor_selection_restore (selection); -} - -static void -undo_redo_table_dialog (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - - WebKitDOMDocument *document; - WebKitDOMElement *table, *element; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - e_html_editor_selection_save (selection); - element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"); - if (!element) - return; - - table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (element), "TABLE"); - - if (!table) { - if ((!event->data.dom.to && undo) || (!event->data.dom.from && !undo)) { - WebKitDOMElement *parent; - - parent = get_parent_block_element (WEBKIT_DOM_NODE (element)); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)), - webkit_dom_node_clone_node (undo ? event->data.dom.from : event->data.dom.to, TRUE), - WEBKIT_DOM_NODE (parent), - NULL); - restore_selection_to_history_event_state (view, event->before); - return; - } else - return; - } - - if (undo) { - if (!event->data.dom.from) - remove_node (WEBKIT_DOM_NODE (table)); - else - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)), - webkit_dom_node_clone_node (event->data.dom.from, TRUE), - WEBKIT_DOM_NODE (table), - NULL); - } else { - if (!event->data.dom.to) - remove_node (WEBKIT_DOM_NODE (table)); - else - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)), - webkit_dom_node_clone_node (event->data.dom.to, TRUE), - WEBKIT_DOM_NODE (table), - NULL); - } - - if (undo) - restore_selection_to_history_event_state (view, event->before); - else - e_html_editor_selection_restore (selection); -} - -static void -undo_redo_table_input (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMElement *element; - WebKitDOMNode *node; - WebKitDOMRange *range; - - selection = e_html_editor_view_get_selection (view); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (!webkit_dom_dom_selection_get_range_count (dom_selection)) { - g_object_unref (dom_selection); - return; - } - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - g_object_unref (dom_selection); - - /* Find if writing into table. */ - node = webkit_dom_range_get_start_container (range, NULL); - if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) - element = WEBKIT_DOM_ELEMENT (node); - else - element = get_parent_block_element (node); - - g_object_unref (range); - - /* If writing to table we have to create different history event. */ - if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (element)) - return; - - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - webkit_dom_node_clone_node (undo ? event->data.dom.from : event->data.dom.to, TRUE), - WEBKIT_DOM_NODE (element), - NULL); - - e_html_editor_selection_restore (selection); -} - -static void -undo_redo_paste (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (undo) { - if (event->type == HISTORY_PASTE_QUOTED) { - WebKitDOMElement *tmp; - WebKitDOMNode *parent; - - restore_selection_to_history_event_state (view, event->after); - - e_html_editor_selection_save (selection); - tmp = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (!tmp) - return; - - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp)); - while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent))) - parent = webkit_dom_node_get_parent_node (parent); - - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (parent), - WEBKIT_DOM_NODE (prepare_paragraph (selection, document, TRUE)), - parent, - NULL); - - e_html_editor_selection_restore (selection); - } else { - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMElement *element, *tmp; - WebKitDOMRange *range; - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - /* Restore the selection how it was before the event occured. */ - range = get_range_for_point (document, event->before.start); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - - e_html_editor_selection_save (selection); - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - remove_node (WEBKIT_DOM_NODE (element)); - - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - webkit_dom_element_remove_attribute (element, "id"); - - range = get_range_for_point (document, event->after.start); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - g_object_unref (dom_selection); - - e_html_editor_selection_save (selection); - - tmp = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - remove_node (WEBKIT_DOM_NODE (tmp)); - - webkit_dom_element_set_id ( - element, "-x-evo-selection-start-marker"); - - e_html_editor_selection_restore (selection); - - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL); - - e_html_editor_view_force_spell_check_for_current_paragraph (view); - } - } else { - restore_selection_to_history_event_state (view, event->before); - - if (event->type == HISTORY_PASTE) - e_html_editor_selection_insert_text (selection, event->data.string.to); - else if (event->type == HISTORY_PASTE_QUOTED) - e_html_editor_view_insert_quoted_text (view, event->data.string.to); - else if (event->type == HISTORY_INSERT_HTML) - e_html_editor_selection_insert_html (selection, event->data.string.to); - else - e_html_editor_selection_insert_as_text (selection, event->data.string.to); - } -} - -static void -undo_redo_image (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range; - - selection = e_html_editor_view_get_selection (view); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (undo) { - WebKitDOMElement *element; - WebKitDOMNode *node; - - range = get_range_for_point (document, event->before.start); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - - e_html_editor_selection_save (selection); - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-end-marker"); - - node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); - - if (WEBKIT_DOM_IS_ELEMENT (node)) - if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-resizable-wrapper") || - element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-smiley-wrapper")) - remove_node (node); - e_html_editor_selection_restore (selection); - } else { - WebKitDOMElement *element; - - range = get_range_for_point (document, event->before.start); - /* Create temporary node on the selection where the delete occured. */ - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - g_object_unref (range); - - e_html_editor_selection_save (selection); - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - /* Insert the deleted content back to the body. */ - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment), TRUE), - WEBKIT_DOM_NODE (element), - NULL); - - e_html_editor_selection_restore (selection); - e_html_editor_view_force_spell_check_for_current_paragraph (view); - } - - g_object_unref (dom_selection); -} - -static void -undo_redo_replace (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - restore_selection_to_history_event_state (view, undo ? event->after : event->before); - - if (undo) { - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "word"); - g_object_unref (dom_selection); - } - - e_html_editor_view_exec_command ( - view, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, - undo ? event->data.string.from : event->data.string.to); - - e_html_editor_view_force_spell_check_for_current_paragraph (view); - - restore_selection_to_history_event_state (view, undo ? event->before : event->after); -} - -static void -undo_redo_replace_all (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - if (undo) { - if (event->type == HISTORY_REPLACE) { - undo_redo_replace (view, event, undo); - return; - } else { - EHTMLEditorViewHistoryEvent *next_event; - GList *next_item; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - - next_item = view->priv->history->next; - - while (next_item) { - next_event = next_item->data; - - if (next_event->type != HISTORY_REPLACE) - break; - - if (g_strcmp0 (next_event->data.string.from, event->data.string.from) != 0) - break; - - if (g_strcmp0 (next_event->data.string.to, event->data.string.to) != 0) - break; - - undo_redo_replace (view, next_event, undo); - - next_item = next_item->next; - } - - view->priv->history = next_item->prev; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); - g_object_unref (dom_window); - g_object_unref (dom_selection); - } - } else { - /* Find if this history item is part of HISTORY_REPLACE_ALL. */ - EHTMLEditorViewHistoryEvent *prev_event; - GList *prev_item; - gboolean replace_all = FALSE; - - prev_item = view->priv->history->prev; - while (prev_item) { - prev_event = prev_item->data; - - if (prev_event->type == HISTORY_REPLACE) - prev_item = prev_item->prev; - else if (prev_event->type == HISTORY_REPLACE_ALL) { - replace_all = TRUE; - break; - } else - break; - } - - if (!replace_all) { - undo_redo_replace (view, event, undo); - return; - } - - prev_item = view->priv->history->prev; - while (prev_item) { - prev_event = prev_item->data; - - if (prev_event->type == HISTORY_REPLACE) { - undo_redo_replace (view, prev_event, undo); - prev_item = prev_item->prev; - } else - break; - } - - view->priv->history = prev_item->next; - } -} - -static void -undo_redo_remove_link (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - - selection = e_html_editor_view_get_selection (view); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - if (undo) { - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMElement *element; - WebKitDOMRange *range; - - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - /* Select the anchor. */ - webkit_dom_dom_selection_modify (dom_selection, "move", "left", "word"); - webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "word"); - - range = html_editor_view_get_dom_range (view); - element = webkit_dom_document_create_element (document, "SPAN", NULL); - webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL); - g_object_unref (range); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment), TRUE), - WEBKIT_DOM_NODE (element), - NULL); - remove_node (WEBKIT_DOM_NODE (element)); - g_object_unref (dom_window); - g_object_unref (dom_selection); - } else - e_html_editor_selection_unlink (selection); - - if (undo) - restore_selection_to_history_event_state (view, event->before); -} - -static void -undo_return_in_empty_list_item (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event) -{ - WebKitDOMDocument *document; - WebKitDOMElement *selection_start_marker; - WebKitDOMNode *parent; - - e_html_editor_selection_save (view->priv->selection); - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - selection_start_marker = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"); - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); - - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (parent)) { - WebKitDOMNode *parent_list; - - remove_selection_markers (document); - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (parent), - webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment), TRUE), - webkit_dom_node_get_next_sibling (parent), - NULL); - - parent_list = parent; - while (node_is_list_or_item (webkit_dom_node_get_parent_node (parent_list))) - parent_list = webkit_dom_node_get_parent_node (parent_list); - - merge_lists_if_possible (parent_list); - } - - e_html_editor_selection_restore (view->priv->selection); -} - -static void -undo_input (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event) -{ - gboolean remove_anchor; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMNode *node, *tmp_node; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - - restore_selection_to_history_event_state (view, event->after); - - webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character"); - if (e_html_editor_selection_is_citation (view->priv->selection)) { - /* Post processing of quoted text in body_input_event_cb needs to be called. */ - view->priv->undo_redo_in_progress = FALSE; - view->priv->dont_save_history_in_body_input = TRUE; - } - - /* If we are undoing the text that was appended to the link we have to - * remove the link and make just the plain text from it. */ - node = webkit_dom_dom_selection_get_anchor_node (dom_selection); - node = webkit_dom_node_get_parent_node (node); - remove_anchor = WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node); - if (remove_anchor) { - gchar *text_content; - - text_content = webkit_dom_node_get_text_content (node); - /* Remove the anchor just in case we are undoing the input from - * the end of it. */ - remove_anchor = - g_utf8_strlen (text_content, -1) == - webkit_dom_dom_selection_get_anchor_offset (dom_selection); - g_free (text_content); - } - - e_html_editor_view_exec_command ( - view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL); - - if (remove_anchor) { - WebKitDOMNode *child; - - /* Don't ask me why, but I got into the situation where the node - * that I received above was out of the document, and all the - * modifications to it were of course not propagated to it. Let's - * get that node again. */ - node = webkit_dom_dom_selection_get_anchor_node (dom_selection); - node = webkit_dom_node_get_parent_node (node); - while ((child = webkit_dom_node_get_first_child (node))) - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), child, node, NULL); - - remove_node (node); - } - - tmp_node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (event->data.fragment)); - if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (tmp_node) && - WEBKIT_DOM_IS_HTMLBR_ELEMENT (webkit_dom_node_get_last_child (tmp_node))) - undo_return_in_empty_list_item (view, event); - - g_object_unref (dom_window); - g_object_unref (dom_selection); -} - -static void -undo_redo_citation_split (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - gboolean in_situ = FALSE; - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (event->before.start.x == event->after.start.x && - event->before.start.y == event->after.start.y && - event->before.end.x == event->after.end.x && - event->before.end.y == event->after.end.y) - in_situ = FALSE; - - if (undo) { - EHTMLEditorSelection *selection; - WebKitDOMElement *selection_start, *parent; - WebKitDOMNode *citation_before, *citation_after, *child, *last_child, *tmp; - - restore_selection_to_history_event_state (view, event->after); - - selection = e_html_editor_view_get_selection (view); - e_html_editor_selection_save (selection); - selection_start = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - if (!selection_start) - return; - - parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start)); - citation_before = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent)); - if (!is_citation_node (citation_before)) { - e_html_editor_selection_restore (selection); - return; - } - - citation_after = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)); - if (!is_citation_node (citation_after)) { - e_html_editor_selection_restore (selection); - return; - } - - /* Get first block in next citation. */ - child = webkit_dom_node_get_first_child (citation_after); - while (child && is_citation_node (child)) - child = webkit_dom_node_get_first_child (child); - - /* Get last block in previous citation. */ - last_child = webkit_dom_node_get_last_child (citation_before); - while (last_child && is_citation_node (last_child)) - last_child = webkit_dom_node_get_last_child (last_child); - - /* Before appending any content to the block, check that the - * last node is not BR, if it is, remove it. */ - tmp = webkit_dom_node_get_last_child (last_child); - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (tmp)) - remove_node (tmp); - - if (in_situ && event->data.fragment) { - webkit_dom_node_append_child ( - webkit_dom_node_get_parent_node (last_child), - webkit_dom_node_clone_node ( - WEBKIT_DOM_NODE (event->data.fragment), TRUE), - NULL); - } else { - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (child)); - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (child)); - - remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child)); - remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child)); - - /* Copy the content of the first block to the last block to get - * to the state how the block looked like before it was split. */ - while ((tmp = webkit_dom_node_get_first_child (child))) - webkit_dom_node_append_child (last_child, tmp, NULL); - - wrap_and_quote_element (view, WEBKIT_DOM_ELEMENT (last_child)); - - remove_node (child); - } - - /* Move all the block from next citation to the previous one. */ - while ((child = webkit_dom_node_get_first_child (citation_after))) - webkit_dom_node_append_child (citation_before, child, NULL); - - remove_selection_markers (document); - - remove_node (WEBKIT_DOM_NODE (parent)); - remove_node (WEBKIT_DOM_NODE (citation_after)); - - /* If enter was pressed when some text was selected, restore it. */ - if (event->data.fragment != NULL && !in_situ) - undo_delete (view, event); - - merge_siblings_if_necessary (document, NULL); - - restore_selection_to_history_event_state (view, event->before); - - e_html_editor_view_force_spell_check_in_viewport (view); - } else { - restore_selection_to_history_event_state (view, event->before); - - if (in_situ) { - WebKitDOMElement *selection_start_marker; - WebKitDOMNode *block; - - e_html_editor_selection_save (view->priv->selection); - - selection_start_marker = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - block = e_html_editor_get_parent_block_node_from_child ( - WEBKIT_DOM_NODE (selection_start_marker)); - remove_selection_markers (document); - - /* Remove current block (and all of its parents if they - * are empty) as it will be replaced by a new block that - * will be in the body and not in the blockquote. */ - remove_node_and_parents_if_empty (block); - } - - insert_new_line_into_citation (view, ""); - } -} - -static void -undo_redo_blockquote (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *element; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - if (undo) - restore_selection_to_history_event_state (view, event->after); - - e_html_editor_selection_save (selection); - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - if (undo) { - WebKitDOMNode *node; - WebKitDOMElement *parent; - - parent = get_parent_block_element (WEBKIT_DOM_NODE (element)); - node = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)); - - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (event->data.fragment), - node, - NULL); - } else { - e_html_editor_selection_set_block_format ( - selection, E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE); - } - - if (undo) - restore_selection_to_history_event_state (view, event->before); - else - e_html_editor_selection_restore (selection); -} - -static void -undo_redo_unquote (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event, - gboolean undo) -{ - EHTMLEditorSelection *selection; - WebKitDOMDocument *document; - WebKitDOMElement *element; - - selection = e_html_editor_view_get_selection (view); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - restore_selection_to_history_event_state (view, undo ? event->after : event->before); - - e_html_editor_selection_save (selection); - element = webkit_dom_document_get_element_by_id ( - document, "-x-evo-selection-start-marker"); - - if (undo) { - WebKitDOMNode *next_sibling, *prev_sibling; - WebKitDOMElement *block; - - block = get_parent_block_element (WEBKIT_DOM_NODE (element)); - - next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (block)); - prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (block)); - - if (prev_sibling && is_citation_node (prev_sibling)) { - webkit_dom_node_append_child ( - prev_sibling, - webkit_dom_node_clone_node (event->data.dom.from, TRUE), - NULL); - - if (next_sibling && is_citation_node (next_sibling)) { - WebKitDOMNode *child; - - while ((child = webkit_dom_node_get_first_child (next_sibling))) - webkit_dom_node_append_child ( - prev_sibling, child, NULL); - - remove_node (next_sibling); - } - } else if (next_sibling && is_citation_node (next_sibling)) { - webkit_dom_node_insert_before ( - next_sibling, - webkit_dom_node_clone_node (event->data.dom.from, TRUE), - webkit_dom_node_get_first_child (next_sibling), - NULL); - } - - remove_node (WEBKIT_DOM_NODE (block)); - } else - move_quoted_block_level_up (view); - - if (undo) - e_html_editor_selection_restore (selection); - else - restore_selection_to_history_event_state (view, event->after); - - e_html_editor_view_force_spell_check_for_current_paragraph (view); -} - -void -e_html_editor_view_redo (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *event; - GList *history; - - if (!e_html_editor_view_can_redo (view)) - return; - - history = view->priv->history; - event = history->prev->data; - d (printf ("\nREDOING EVENT:\n")); - d (print_history_event (event)); - - view->priv->undo_redo_in_progress = TRUE; - - switch (event->type) { - case HISTORY_BOLD: - case HISTORY_MONOSPACE: - case HISTORY_STRIKETHROUGH: - case HISTORY_UNDERLINE: - case HISTORY_ALIGNMENT: - case HISTORY_BLOCK_FORMAT: - case HISTORY_FONT_SIZE: - case HISTORY_ITALIC: - undo_redo_style_change (view, event, FALSE); - break; - case HISTORY_DELETE: - redo_delete (view, event); - break; - case HISTORY_INDENT: - undo_redo_indent (view, event, FALSE); - break; - case HISTORY_INPUT: - undo_delete (view, event); - { - gchar *text_content; - WebKitDOMRange *range; - WebKitDOMNode *first_child; - - range = html_editor_view_get_dom_range (view); - html_editor_view_check_magic_smileys (view, range); - - first_child = webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (event->data.fragment)); - text_content = webkit_dom_node_get_text_content (first_child); - /* Call magic links when the space was pressed. */ - if (g_str_has_prefix (text_content, UNICODE_NBSP)) { - view->priv->space_key_pressed = TRUE; - html_editor_view_check_magic_links (view, range, FALSE); - view->priv->space_key_pressed = FALSE; - } - g_free (text_content); - g_object_unref (range); - } - break; - case HISTORY_REMOVE_LINK: - undo_redo_remove_link (view, event, FALSE); - break; - case HISTORY_FONT_COLOR: - undo_redo_font_color (view, event, FALSE); - break; - case HISTORY_CITATION_SPLIT: - undo_redo_citation_split (view, event, FALSE); - break; - case HISTORY_PASTE: - case HISTORY_PASTE_AS_TEXT: - case HISTORY_PASTE_QUOTED: - case HISTORY_INSERT_HTML: - undo_redo_paste (view, event, FALSE); - break; - case HISTORY_IMAGE: - case HISTORY_SMILEY: - undo_redo_image (view, event, FALSE); - break; - case HISTORY_WRAP: - undo_redo_wrap (view, event, FALSE); - break; - case HISTORY_IMAGE_DIALOG: - undo_redo_image_dialog (view, event, FALSE); - break; - case HISTORY_LINK_DIALOG: - undo_redo_link_dialog (view, event, FALSE); - break; - case HISTORY_TABLE_DIALOG: - undo_redo_table_dialog (view, event, FALSE); - break; - case HISTORY_TABLE_INPUT: - undo_redo_table_input (view, event, FALSE); - break; - case HISTORY_PAGE_DIALOG: - undo_redo_page_dialog (view, event, FALSE); - break; - case HISTORY_HRULE_DIALOG: - undo_redo_hrule_dialog (view, event, FALSE); - break; - case HISTORY_REPLACE: - case HISTORY_REPLACE_ALL: - undo_redo_replace_all (view, event, FALSE); - break; - case HISTORY_BLOCKQUOTE: - undo_redo_blockquote (view, event, FALSE); - break; - case HISTORY_UNQUOTE: - undo_redo_unquote (view, event, FALSE); - break; - case HISTORY_AND: - g_warning ("Unhandled HISTORY_AND event!"); - break; - default: - return; - } - - if (history->prev->prev) { - event = history->prev->prev->data; - if (event->type == HISTORY_AND) { - view->priv->history = history->prev->prev; - e_html_editor_view_redo (view); - return; - } - } - - view->priv->history = view->priv->history->prev; - - d (print_redo_events (view)); - - html_editor_view_user_changed_contents_cb (view); - - view->priv->undo_redo_in_progress = FALSE; -} - -void -e_html_editor_view_undo (EHTMLEditorView *view) -{ - EHTMLEditorViewHistoryEvent *event; - GList *history; - - if (!e_html_editor_view_can_undo (view)) - return; - - history = view->priv->history; - event = history->data; - d (printf ("\nUNDOING EVENT:\n")); - d (print_history_event (event)); - - view->priv->undo_redo_in_progress = TRUE; - - switch (event->type) { - case HISTORY_BOLD: - case HISTORY_ITALIC: - case HISTORY_STRIKETHROUGH: - case HISTORY_UNDERLINE: - case HISTORY_FONT_SIZE: - if (event_selection_was_collapsed (event)) { - if (history->next) { - view->priv->history = history->next; - e_html_editor_view_undo (view); - } - view->priv->undo_redo_in_progress = FALSE; - return; - } - case HISTORY_ALIGNMENT: - case HISTORY_BLOCK_FORMAT: - case HISTORY_MONOSPACE: - undo_redo_style_change (view, event, TRUE); - break; - case HISTORY_DELETE: - undo_delete (view, event); - break; - case HISTORY_INDENT: - undo_redo_indent (view, event, TRUE); - break; - case HISTORY_INPUT: - undo_input (view, event); - break; - case HISTORY_REMOVE_LINK: - undo_redo_remove_link (view, event, TRUE); - break; - case HISTORY_FONT_COLOR: - undo_redo_font_color (view, event, TRUE); - break; - case HISTORY_CITATION_SPLIT: - undo_redo_citation_split (view, event, TRUE); - break; - case HISTORY_PASTE: - case HISTORY_PASTE_AS_TEXT: - case HISTORY_PASTE_QUOTED: - case HISTORY_INSERT_HTML: - undo_redo_paste (view, event, TRUE); - break; - case HISTORY_IMAGE: - case HISTORY_SMILEY: - undo_redo_image (view, event, TRUE); - break; - case HISTORY_WRAP: - undo_redo_wrap (view, event, TRUE); - break; - case HISTORY_IMAGE_DIALOG: - undo_redo_image_dialog (view, event, TRUE); - break; - case HISTORY_LINK_DIALOG: - undo_redo_link_dialog (view, event, TRUE); - break; - case HISTORY_TABLE_DIALOG: - undo_redo_table_dialog (view, event, TRUE); - break; - case HISTORY_TABLE_INPUT: - undo_redo_table_input (view, event, TRUE); - break; - case HISTORY_PAGE_DIALOG: - undo_redo_page_dialog (view, event, TRUE); - break; - case HISTORY_HRULE_DIALOG: - undo_redo_hrule_dialog (view, event, TRUE); - break; - case HISTORY_REPLACE: - case HISTORY_REPLACE_ALL: - undo_redo_replace_all (view, event, TRUE); - break; - case HISTORY_BLOCKQUOTE: - undo_redo_blockquote (view, event, TRUE); - break; - case HISTORY_UNQUOTE: - undo_redo_unquote (view, event, TRUE); - break; - case HISTORY_AND: - g_warning ("Unhandled HISTORY_AND event!"); - break; - default: - return; - } - - event = history->next->data; - if (event->type == HISTORY_AND) { - view->priv->history = history->next->next; - e_html_editor_view_undo (view); - return; - } - - if (history->next) - view->priv->history = view->priv->history->next; - - d (print_undo_events (view)); - - html_editor_view_user_changed_contents_cb (view); - - view->priv->undo_redo_in_progress = FALSE; -} - -/* Following functions are used by EHTMLEditorPageDialog to get the right colors - * when view is not focused */ -void -e_html_editor_view_block_style_updated_callbacks (EHTMLEditorView *view) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (!view->priv->style_change_callbacks_blocked) { - g_signal_handlers_block_by_func (view, style_updated_cb, NULL); - view->priv->style_change_callbacks_blocked = TRUE; - } -} - -void -e_html_editor_view_unblock_style_updated_callbacks (EHTMLEditorView *view) -{ - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); - - if (view->priv->style_change_callbacks_blocked) { - g_signal_handlers_unblock_by_func (view, style_updated_cb, NULL); - view->priv->style_change_callbacks_blocked = FALSE; - } -} - -gboolean -e_html_editor_view_is_pasting_content_from_itself (EHTMLEditorView *view) -{ - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE); - - if (view->priv->pasting_primary_clipboard) - return view->priv->copy_paste_primary_in_view; - else - return view->priv->copy_paste_clipboard_in_view; -} - -void -e_html_editor_view_remove_input_event_listener_from_body (EHTMLEditorView *view) -{ - if (!view->priv->body_input_event_removed) { - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - webkit_dom_event_target_remove_event_listener ( - WEBKIT_DOM_EVENT_TARGET ( - webkit_dom_document_get_body (document)), - "input", - G_CALLBACK (body_input_event_cb), - FALSE); - - view->priv->body_input_event_removed = TRUE; - } -} - -void -e_html_editor_view_register_input_event_listener_on_body (EHTMLEditorView *view) -{ - if (view->priv->body_input_event_removed) { - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET ( - webkit_dom_document_get_body (document)), - "input", - G_CALLBACK (body_input_event_cb), - FALSE, - view); - - view->priv->body_input_event_removed = FALSE; - } -} - -static WebKitDOMElement * -prepare_top_signature_spacer (EHTMLEditorSelection *selection, - WebKitDOMDocument *document) -{ - WebKitDOMElement *element; - - element = prepare_paragraph (selection, document, FALSE); - webkit_dom_element_remove_attribute (element, "id"); - element_add_class (element, "-x-evo-top-signature-spacer"); - - return element; -} - -static void -move_caret_after_signature_inserted (EHTMLEditorView *view) -{ - EHTMLEditorSelection *selection; - gboolean start_bottom, top_signature; - gboolean has_paragraphs_in_body = TRUE; - WebKitDOMDocument *document; - WebKitDOMElement *element, *signature; - WebKitDOMHTMLElement *body; - WebKitDOMNodeList *list; - - /* When there is an option composer-reply-start-bottom set we have - * to move the caret between reply and signature. */ - start_bottom = start_typing_at_bottom (view); - selection = e_html_editor_view_get_selection (view); - - top_signature = - use_top_signature (view) && - !view->priv->is_message_from_edit_as_new && - !view->priv->is_new_message; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - body = webkit_dom_document_get_body (document); - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), "data-message", "", NULL); - - /* If editing message as new don't handle with caret */ - if (view->priv->is_message_from_edit_as_new || view->priv->is_message_from_draft) { - if (view->priv->is_message_from_edit_as_new) - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), - "data-edit-as-new", - "", - NULL); - - if (view->priv->is_message_from_edit_as_new && !view->priv->is_message_from_draft) { - element = WEBKIT_DOM_ELEMENT (body); - e_html_editor_selection_block_selection_changed (selection); - goto move_caret; - } else - e_html_editor_selection_scroll_to_caret (selection); - - return; - } - - e_html_editor_selection_block_selection_changed (selection); - - list = webkit_dom_document_get_elements_by_class_name (document, "-x-evo-paragraph"); - signature = webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL); - /* Situation when wrapped paragraph is just in signature and not in message body */ - if (webkit_dom_node_list_get_length (list) == 1) - if (signature && webkit_dom_element_query_selector (signature, ".-x-evo-paragraph", NULL)) - has_paragraphs_in_body = FALSE; - - /* - * - * Keeping Signatures in the beginning of composer - * ------------------------------------------------ - * - * Purists are gonna blast me for this. - * But there are so many people (read Outlook users) who want this. - * And Evo is an exchange-client, Outlook-replacement etc. - * So Here it goes :( - * - * -- Sankar - * - */ - if (signature && top_signature) { - WebKitDOMElement *spacer; - - spacer = prepare_top_signature_spacer (selection, document); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (spacer), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (signature)), - NULL); - } - - if (webkit_dom_node_list_get_length (list) == 0) - has_paragraphs_in_body = FALSE; - - element = webkit_dom_document_get_element_by_id (document, "-x-evo-input-start"); - if (!signature) { - if (start_bottom) { - if (!element) { - element = prepare_paragraph (selection, document, FALSE); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (element), - NULL); - } - } else - element = WEBKIT_DOM_ELEMENT (body); - - g_object_unref (list); - goto move_caret; - } - - if (!has_paragraphs_in_body) { - element = prepare_paragraph (selection, document, FALSE); - if (top_signature) { - if (start_bottom) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (element), - NULL); - } else { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (signature), - NULL); - } - } else { - if (start_bottom) - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (signature), - NULL); - else - element = WEBKIT_DOM_ELEMENT (body); - } - } else { - if (!element && top_signature) { - element = prepare_paragraph (selection, document, FALSE); - if (start_bottom) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (element), - NULL); - } else { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (signature), - NULL); - } - } else if (element && top_signature && !start_bottom) { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (signature), - NULL); - } else if (element && start_bottom) { - /* Leave it how it is */ - } else - element = WEBKIT_DOM_ELEMENT (body); - } - - g_object_unref (list); - - move_caret: - if (element) { - WebKitDOMDOMSelection *dom_selection; - WebKitDOMDOMWindow *dom_window; - WebKitDOMRange *range; - - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - range = webkit_dom_document_create_range (document); - webkit_dom_range_select_node_contents ( - range, WEBKIT_DOM_NODE (element), NULL); - webkit_dom_range_collapse (range, TRUE, NULL); - webkit_dom_dom_selection_remove_all_ranges (dom_selection); - webkit_dom_dom_selection_add_range (dom_selection, range); - - g_clear_object (&dom_selection); - g_clear_object (&dom_window); - g_clear_object (&range); - } - - if (start_bottom) - e_html_editor_selection_scroll_to_caret (selection); - - e_html_editor_view_force_spell_check_in_viewport (view); - - e_html_editor_selection_unblock_selection_changed (selection); -} - -gchar * -e_html_editor_view_insert_signature (EHTMLEditorView *view, - const gchar *content, - gboolean is_html, - const gchar *id, - gboolean *set_signature_from_message, - gboolean *check_if_signature_is_changed, - gboolean *ignore_next_signature_change) -{ - gchar *new_signature_id = NULL; - gchar *signature_text = NULL; - gboolean top_signature; - gulong list_length, ii; - WebKitDOMDocument *document; - WebKitDOMElement *signature_to_insert; - WebKitDOMElement *insert_signature_in = NULL; - WebKitDOMElement *signature_wrapper = NULL; - WebKitDOMElement *element, *converted_signature = NULL; - WebKitDOMHTMLElement *body; - WebKitDOMNodeList *signatures; - - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL); - g_return_val_if_fail (set_signature_from_message != NULL, NULL); - g_return_val_if_fail (check_if_signature_is_changed != NULL, NULL); - g_return_val_if_fail (ignore_next_signature_change != NULL, NULL); - - /* "Edit as New Message" sets is_message_from_edit_as_new. - * Always put the signature at the bottom for that case. */ - top_signature = - use_top_signature (view) && - !view->priv->is_message_from_edit_as_new && - !view->priv->is_new_message; - - /* Create the DOM signature that is the same across all types of signatures. */ - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - body = webkit_dom_document_get_body (document); - signature_to_insert = webkit_dom_document_create_element (document, "span", NULL); - webkit_dom_element_set_class_name (signature_to_insert, "-x-evo-signature"); - /* The combo box active ID is the signature's ESource UID. */ - webkit_dom_element_set_id (signature_to_insert, id); - insert_signature_in = signature_to_insert; - - /* The signature has no content usually it means it is set to None. */ - if (!content) - goto insert; - - if (!is_html) { - gchar *html; - - html = camel_text_to_html (content, 0, 0); - if (html) { - signature_text = html; - } else - signature_text = g_strdup (content); - - insert_signature_in = webkit_dom_document_create_element (document, "pre", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (signature_to_insert), - WEBKIT_DOM_NODE (insert_signature_in), - NULL); - } else - signature_text = g_strdup (content); - - /* If inserting HTML signature in the plain text composer we have to convert it. */ - if (is_html && !view->priv->html_mode && !strstr (signature_text, "data-evo-signature-plain-text-mode")) { - gchar *inner_text; - - /* Save the converted signature to avoid parsing it later again - * while inserting it into the view. */ - converted_signature = webkit_dom_document_create_element (document, "pre", NULL); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (converted_signature), signature_text, NULL); - e_html_editor_view_convert_element_from_html_to_plain_text (view, converted_signature); - inner_text = webkit_dom_html_element_get_inner_text (WEBKIT_DOM_HTML_ELEMENT (converted_signature)); - - g_free (signature_text); - signature_text = inner_text ? g_strstrip (inner_text) : g_strdup (""); - /* because of the -- \n check */ - is_html = FALSE; - } - - /* The signature dash convention ("-- \n") is specified - * in the "Son of RFC 1036", section 4.3.2. - * http://www.chemie.fu-berlin.de/outerspace/netnews/son-of-1036.html - */ - if (add_signature_delimiter (view)) { - const gchar *delim; - const gchar *delim_nl; - - if (is_html) { - delim = "-- <BR>"; - delim_nl = "\n-- <BR>"; - } else { - delim = "-- \n"; - delim_nl = "\n-- \n"; - } - - /* Skip the delimiter if the signature already has one. */ - if (g_ascii_strncasecmp (signature_text, delim, strlen (delim)) == 0) - ; /* skip */ - else if (e_util_strstrcase (signature_text, delim_nl) != NULL) - ; /* skip */ - else { - WebKitDOMElement *pre_delimiter; - - pre_delimiter = webkit_dom_document_create_element (document, "pre", NULL); - /* Always use the HTML delimiter as we are never in anything - * like a strict plain text mode. */ - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (pre_delimiter), "-- <br>", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (insert_signature_in), - WEBKIT_DOM_NODE (pre_delimiter), - NULL); - } - } - - if (converted_signature) { - WebKitDOMNode *node; - - while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (converted_signature)))) - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (insert_signature_in), node, NULL); - remove_node (WEBKIT_DOM_NODE (converted_signature)); - } else - webkit_dom_html_element_insert_adjacent_html ( - WEBKIT_DOM_HTML_ELEMENT (insert_signature_in), "beforeend", signature_text, NULL); - - element = webkit_dom_element_query_selector ( - insert_signature_in, "[data-evo-signature-plain-text-mode]", NULL); - if (element) - webkit_dom_element_remove_attribute ( - element, "data-evo-signature-plain-text-mode"); - g_free (signature_text); - -insert: - /* Remove the old signature and insert the new one. */ - signatures = webkit_dom_document_get_elements_by_class_name ( - document, "-x-evo-signature-wrapper"); - list_length = webkit_dom_node_list_get_length (signatures); - for (ii = 0; ii < list_length; ii++) { - WebKitDOMNode *wrapper, *signature; - - wrapper = webkit_dom_node_list_item (signatures, ii); - signature = webkit_dom_node_get_first_child (wrapper); - - /* Old messages will have the signature id in the name attribute, correct it. */ - dom_element_rename_attribute (WEBKIT_DOM_ELEMENT (signature), "name", "id"); - - /* When we are editing a message with signature, we need to unset the - * active signature id as if the signature in the message was edited - * by the user we would discard these changes. */ - if (*set_signature_from_message && content && - (view->priv->is_message_from_edit_as_new || view->priv->is_message_from_draft)) { - if (*check_if_signature_is_changed) { - /* Normalize the signature that we want to insert as the one in the - * message already is normalized. */ - webkit_dom_node_normalize (WEBKIT_DOM_NODE (signature_to_insert)); - if (!webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (signature_to_insert), signature)) { - /* Signature in the body is different than the one with the - * same id, so set the active signature to None and leave - * the signature that is in the body. */ - new_signature_id = g_strdup ("none"); - *ignore_next_signature_change = TRUE; - } - - *check_if_signature_is_changed = FALSE; - *set_signature_from_message = FALSE; - } else { - /* Load the signature and check if is it the same - * as the signature in body or the user previously - * changed it. */ - new_signature_id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (signature)); - *check_if_signature_is_changed = TRUE; - } - g_object_unref (wrapper); - g_object_unref (signatures); - - return new_signature_id; - } - - /* If the top signature was set we have to remove the newline - * that was inserted after it */ - if (top_signature) { - WebKitDOMElement *spacer; - - spacer = webkit_dom_document_query_selector ( - document, ".-x-evo-top-signature-spacer", NULL); - if (spacer) - remove_node_if_empty (WEBKIT_DOM_NODE (spacer)); - } - - /* Leave just one signature wrapper there as it will be reused. */ - if (ii != list_length - 1) { - g_object_unref (wrapper); - remove_node (wrapper); - } else { - remove_node (signature); - signature_wrapper = WEBKIT_DOM_ELEMENT (wrapper); - } - } - - if (signature_wrapper) { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (signature_wrapper), - WEBKIT_DOM_NODE (signature_to_insert), - NULL); - - /* Insert a spacer below the top signature */ - if (top_signature && content) { - WebKitDOMElement *spacer; - - spacer = prepare_top_signature_spacer (view->priv->selection, document); - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (spacer), - webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (signature_wrapper)), - NULL); - } - - g_object_unref (signature_wrapper); - } else { - signature_wrapper = webkit_dom_document_create_element (document, "div", NULL); - webkit_dom_element_set_class_name (signature_wrapper, "-x-evo-signature-wrapper"); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (signature_wrapper), - WEBKIT_DOM_NODE (signature_to_insert), - NULL); - - if (top_signature) { - WebKitDOMNode *child; - - child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); - - if (start_typing_at_bottom (view)) { - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (signature_wrapper), - child, - NULL); - } else { - /* When we are using signature on top the caret - * should be before the signature */ - webkit_dom_node_insert_before ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (signature_wrapper), - child, - NULL); - } - } else { - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (body), - WEBKIT_DOM_NODE (signature_wrapper), - NULL); - } - - move_caret_after_signature_inserted (view); - } - g_object_unref (signatures); - - if (is_html && view->priv->html_mode) - e_html_editor_view_fix_file_uri_images (view); - - /* Make sure the flag will be unset and won't influence user's choice */ - *set_signature_from_message = FALSE; - - return NULL; -} - -/** - * e_html_editor_view_is_ready; - * @view: an #EHTMLEditorView - * - * Checks the current load status of the view. - * - * Returns: TRUE when the view is loaded and ready for manipulations. - */ -gboolean -e_html_editor_view_is_ready (EHTMLEditorView *view) -{ - WebKitLoadStatus status; - - status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view)); - - return status == WEBKIT_LOAD_FINISHED; -} diff --git a/e-util/e-html-editor-view.h b/e-util/e-html-editor-view.h deleted file mode 100644 index 724ef24..0000000 --- a/e-util/e-html-editor-view.h +++ /dev/null @@ -1,342 +0,0 @@ -/* - * e-html-editor-view.h - * - * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com> - * - * 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 <http://www.gnu.org/licenses/> - * - */ - -#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION) -#error "Only <e-util/e-util.h> should be included directly." -#endif - -#ifndef E_HTML_EDITOR_VIEW_H -#define E_HTML_EDITOR_VIEW_H - -#include <webkit/webkit.h> - -#include <camel/camel.h> - -#include <e-util/e-html-editor-selection.h> -#include <e-util/e-emoticon.h> -#include <e-util/e-spell-checker.h> -#include <e-util/e-util-enums.h> - -/* Standard GObject macros */ -#define E_TYPE_HTML_EDITOR_VIEW \ - (e_html_editor_view_get_type ()) -#define E_HTML_EDITOR_VIEW(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorView)) -#define E_HTML_EDITOR_VIEW_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewClass)) -#define E_IS_HTML_EDITOR_VIEW(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_HTML_EDITOR_VIEW)) -#define E_IS_HTML_EDITOR_VIEW_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_HTML_EDITOR_VIEW)) -#define E_HTML_EDITOR_VIEW_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewClass)) - -#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b" -#define UNICODE_NBSP "\xc2\xa0" - -#define SPACES_PER_INDENTATION 3 -#define SPACES_PER_LIST_LEVEL 3 -#define SPACES_ORDERED_LIST_FIRST_LEVEL 6 -#define MINIMAL_PARAGRAPH_WIDTH 5 -#define TAB_LENGTH 8 - -G_BEGIN_DECLS - -typedef struct _EHTMLEditorView EHTMLEditorView; -typedef struct _EHTMLEditorViewClass EHTMLEditorViewClass; -typedef struct _EHTMLEditorViewPrivate EHTMLEditorViewPrivate; - -struct _EHTMLEditorView { - WebKitWebView parent; - EHTMLEditorViewPrivate *priv; -}; - -struct _EHTMLEditorViewClass { - WebKitWebViewClass parent_class; - - void (*paste_clipboard_quoted) - (EHTMLEditorView *view); - gboolean (*popup_event) (EHTMLEditorView *view, - GdkEventButton *event); - void (*paste_primary_clipboard) - (EHTMLEditorView *view); - void (*is_ready) (EHTMLEditorView *view); -}; - -enum EHTMLEditorViewHistoryEventType { - HISTORY_ALIGNMENT, - HISTORY_AND, - HISTORY_BLOCK_FORMAT, - HISTORY_BLOCKQUOTE, - HISTORY_BOLD, - HISTORY_CELL_DIALOG, - HISTORY_DELETE, /* BackSpace, Delete, with and without selection */ - HISTORY_FONT_COLOR, - HISTORY_FONT_SIZE, - HISTORY_HRULE_DIALOG, - HISTORY_INDENT, - HISTORY_INPUT, - HISTORY_IMAGE, - HISTORY_IMAGE_DIALOG, - HISTORY_INSERT_HTML, - HISTORY_ITALIC, - HISTORY_LINK_DIALOG, - HISTORY_MONOSPACE, - HISTORY_PAGE_DIALOG, - HISTORY_PASTE, - HISTORY_PASTE_AS_TEXT, - HISTORY_PASTE_QUOTED, - HISTORY_REMOVE_LINK, - HISTORY_REPLACE, - HISTORY_REPLACE_ALL, - HISTORY_CITATION_SPLIT, - HISTORY_SMILEY, - HISTORY_START, /* Start of history */ - HISTORY_STRIKETHROUGH, - HISTORY_TABLE_DIALOG, - HISTORY_TABLE_INPUT, - HISTORY_UNDERLINE, - HISTORY_WRAP, - HISTORY_UNQUOTE -}; - -typedef struct { - gint from; /* From what format we are changing. */ - gint to; /* To what format we are changing. */ -} EHTMLEditorViewStyleChange; - -/* This is used for e-html-editor-*-dialogs */ -typedef struct { - WebKitDOMNode *from; /* From what node we are changing. */ - WebKitDOMNode *to; /* To what node we are changing. */ -} EHTMLEditorViewDOMChange; - -typedef struct { - gchar *from; /* From what format we are changing. */ - gchar *to; /* To what format we are changing. */ -} EHTMLEditorViewStringChange; - -typedef struct { - guint x; - guint y; -} EHTMLEditorViewSelectionPoint; - -typedef struct { - EHTMLEditorViewSelectionPoint start; - EHTMLEditorViewSelectionPoint end; -} EHTMLEditorViewSelection; - -typedef struct { - enum EHTMLEditorViewHistoryEventType type; - EHTMLEditorViewSelection before; - EHTMLEditorViewSelection after; - union { - WebKitDOMDocumentFragment *fragment; - EHTMLEditorViewStyleChange style; - EHTMLEditorViewStringChange string; - EHTMLEditorViewDOMChange dom; - } data; -} EHTMLEditorViewHistoryEvent; - -GType e_html_editor_view_get_type (void) G_GNUC_CONST; -EHTMLEditorView * - e_html_editor_view_new (void); -EHTMLEditorSelection * - e_html_editor_view_get_selection - (EHTMLEditorView *view); -gboolean e_html_editor_view_exec_command (EHTMLEditorView *view, - EHTMLEditorViewCommand command, - const gchar *value); -gboolean e_html_editor_view_get_changed (EHTMLEditorView *view); -void e_html_editor_view_set_changed (EHTMLEditorView *view, - gboolean changed); -gboolean e_html_editor_view_get_html_mode - (EHTMLEditorView *view); -void e_html_editor_view_set_html_mode - (EHTMLEditorView *view, - gboolean html_mode); -void e_html_editor_view_save_history_for_drop - (EHTMLEditorView *view); -gboolean e_html_editor_view_get_inline_spelling - (EHTMLEditorView *view); -void e_html_editor_view_set_inline_spelling - (EHTMLEditorView *view, - gboolean inline_spelling); -gboolean e_html_editor_view_get_magic_links - (EHTMLEditorView *view); -void e_html_editor_view_set_magic_links - (EHTMLEditorView *view, - gboolean magic_links); -void e_html_editor_view_insert_smiley - (EHTMLEditorView *view, - EEmoticon *emoticon); -gboolean e_html_editor_view_get_magic_smileys - (EHTMLEditorView *view); -void e_html_editor_view_set_magic_smileys - (EHTMLEditorView *view, - gboolean magic_smileys); -gboolean e_html_editor_view_get_unicode_smileys - (EHTMLEditorView *view); -void e_html_editor_view_set_unicode_smileys - (EHTMLEditorView *view, - gboolean unicode_smileys); -ESpellChecker * e_html_editor_view_get_spell_checker - (EHTMLEditorView *view); -gchar * e_html_editor_view_get_text_html - (EHTMLEditorView *view, - const gchar *from_domain, - GList **inline_images); -gchar * e_html_editor_view_get_text_html_for_drafts_with_images - (EHTMLEditorView *view, - const gchar *from_domain, - GList **inline_images); -gchar * e_html_editor_view_get_text_html_for_drafts - (EHTMLEditorView *view); -gchar * e_html_editor_view_get_body_text_html_for_drafts - (EHTMLEditorView *view); -gchar * e_html_editor_view_get_text_plain - (EHTMLEditorView *view); -void e_html_editor_view_convert_and_insert_plain_text - (EHTMLEditorView *view, - const gchar *text); -void e_html_editor_view_convert_and_insert_html_to_plain_text - (EHTMLEditorView *view, - const gchar *html); -void e_html_editor_view_convert_element_from_html_to_plain_text - (EHTMLEditorView *view, - WebKitDOMElement *element); -void e_html_editor_view_set_text_html - (EHTMLEditorView *view, - const gchar *text); -void e_html_editor_view_set_text_plain - (EHTMLEditorView *view, - const gchar *text); -void e_html_editor_view_paste_as_text - (EHTMLEditorView *view); -void e_html_editor_view_paste_clipboard_quoted - (EHTMLEditorView *view); -void e_html_editor_view_embed_styles (EHTMLEditorView *view); -void e_html_editor_view_remove_embed_styles - (EHTMLEditorView *view); -void e_html_editor_view_update_fonts (EHTMLEditorView *view); -void e_html_editor_view_check_magic_links - (EHTMLEditorView *view, - gboolean while_typing); -WebKitDOMElement * - e_html_editor_view_quote_plain_text_element - (EHTMLEditorView *view, - WebKitDOMElement *element); -WebKitDOMElement * - e_html_editor_view_quote_plain_text - (EHTMLEditorView *view); -void e_html_editor_view_dequote_plain_text - (EHTMLEditorView *view); -void e_html_editor_view_turn_spell_check_off - (EHTMLEditorView *view); -void e_html_editor_view_force_spell_check_for_current_paragraph - (EHTMLEditorView *view); -void e_html_editor_view_force_spell_check - (EHTMLEditorView *view); -void e_html_editor_view_force_spell_check_in_viewport - (EHTMLEditorView *view); -void e_html_editor_view_quote_plain_text_element_after_wrapping - (WebKitDOMDocument *document, - WebKitDOMElement *element, - gint quote_level); -void e_html_editor_view_add_inline_image_from_mime_part - (EHTMLEditorView *view, - CamelMimePart *part); -void remove_image_attributes_from_element - (WebKitDOMElement *element); -gboolean e_html_editor_view_is_message_from_draft - (EHTMLEditorView *view); -void e_html_editor_view_set_is_editting_message - (EHTMLEditorView *view, - gboolean value); -void e_html_editor_view_set_is_message_from_draft - (EHTMLEditorView *view, - gboolean value); -void e_html_editor_view_set_is_message_from_selection - (EHTMLEditorView *view, - gboolean value); -gboolean e_html_editor_view_is_message_from_edit_as_new - (EHTMLEditorView *view); -void e_html_editor_view_set_is_message_from_edit_as_new - (EHTMLEditorView *view, - gboolean value); -gboolean e_html_editor_view_content_is_new_message - (EHTMLEditorView *view); -void e_html_editor_view_set_content_is_new_message - (EHTMLEditorView *view, - gboolean value); -void e_html_editor_view_insert_quoted_text - (EHTMLEditorView *view, - const gchar *text); -void e_html_editor_view_set_link_color - (EHTMLEditorView *view, - GdkRGBA *color); -void e_html_editor_view_set_visited_link_color - (EHTMLEditorView *view, - GdkRGBA *color); -void e_html_editor_view_fix_file_uri_images - (EHTMLEditorView *view); -gboolean e_html_editor_view_can_undo (EHTMLEditorView *view); -void e_html_editor_view_undo (EHTMLEditorView *view); -gboolean e_html_editor_view_can_redo (EHTMLEditorView *view); -void e_html_editor_view_redo (EHTMLEditorView *view); -void e_html_editor_view_insert_new_history_event - (EHTMLEditorView *view, - EHTMLEditorViewHistoryEvent *event); -void e_html_editor_view_clear_history - (EHTMLEditorView *view); -gboolean e_html_editor_view_is_undo_redo_in_progress - (EHTMLEditorView *view); -void e_html_editor_view_set_undo_redo_in_progress - (EHTMLEditorView *view, - gboolean value); -void e_html_editor_view_block_style_updated_callbacks - (EHTMLEditorView *view); -void e_html_editor_view_unblock_style_updated_callbacks - (EHTMLEditorView *view); -gboolean e_html_editor_view_is_pasting_content_from_itself - (EHTMLEditorView *view); -void e_html_editor_view_remove_input_event_listener_from_body - (EHTMLEditorView *view); -void e_html_editor_view_register_input_event_listener_on_body - (EHTMLEditorView *view); -gchar * e_html_editor_view_insert_signature - (EHTMLEditorView *view, - const gchar *content, - gboolean is_html, - const gchar *id, - gboolean *set_signature_from_message, - gboolean *check_if_signature_is_changed, - gboolean *ignore_next_signature_change); -gboolean e_html_editor_view_is_ready (EHTMLEditorView *view); -void e_html_editor_view_reconnect_paste_clipboard_signals - (EHTMLEditorView *view); -G_END_DECLS - -#endif /* E_HTML_EDITOR_VIEW_H */ diff --git a/e-util/e-html-editor.c b/e-util/e-html-editor.c index 90c8069..bb6af69 100644 --- a/e-util/e-html-editor.c +++ b/e-util/e-html-editor.c @@ -32,8 +32,9 @@ #include "e-alert-dialog.h" #include "e-alert-sink.h" #include "e-html-editor-private.h" -#include "e-html-editor-utils.h" -#include "e-html-editor-selection.h" +#include "e-content-editor.h" +#include "e-misc-utils.h" +#include "e-simple-async-result.h" #define E_HTML_EDITOR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -42,8 +43,8 @@ /** * EHTMLEditor: * - * #EHTMLEditor provides GUI for manipulating with properties of #EHTMLEditorView and - * its #EHTMLEditorSelection - i.e. toolbars and actions. + * #EHTMLEditor provides GUI for manipulating with properties of + * #EContentEditor i.e. toolbars and actions. */ /* This controls how spelling suggestions are divided between the primary @@ -88,6 +89,8 @@ G_DEFINE_TYPE_WITH_CODE ( e_html_editor, GTK_TYPE_GRID, G_IMPLEMENT_INTERFACE ( + E_TYPE_EXTENSIBLE, NULL) + G_IMPLEMENT_INTERFACE ( E_TYPE_ALERT_SINK, e_html_editor_alert_sink_init)) @@ -97,25 +100,21 @@ static void action_context_spell_suggest_cb (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; const gchar *word; word = g_object_get_data (G_OBJECT (action), "word"); g_return_if_fail (word != NULL); - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - - e_html_editor_selection_replace_caret_word (selection, word); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_replace_caret_word (cnt_editor, word); } static void html_editor_inline_spelling_suggestions (EHTMLEditor *editor) { - EHTMLEditorView *view; - EHTMLEditorSelection *selection; - WebKitSpellChecker *checker; + EContentEditor *cnt_editor; + ESpellChecker *spell_checker; GtkActionGroup *action_group; GtkUIManager *manager; gchar **suggestions; @@ -127,15 +126,13 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor) guint threshold; gint ii; - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ()); - - word = e_html_editor_selection_get_caret_word (selection); + cnt_editor = e_html_editor_get_content_editor (editor); + word = e_content_editor_get_caret_word (cnt_editor); if (word == NULL || *word == '\0') return; - suggestions = webkit_spell_checker_get_guesses_for_word (checker, word, NULL); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); + suggestions = e_spell_checker_get_guesses_for_word (spell_checker, word); path = "/context-menu/context-spell-suggest/"; manager = e_html_editor_get_ui_manager (editor); @@ -205,6 +202,7 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor) g_free (word); g_strfreev (suggestions); + g_clear_object (&spell_checker); } /* Helper for html_editor_update_actions() */ @@ -212,10 +210,9 @@ static void html_editor_spell_checkers_foreach (EHTMLEditor *editor, const gchar *language_code) { - EHTMLEditorView *view; - EHTMLEditorSelection *selection; + EContentEditor *cnt_editor; ESpellChecker *spell_checker; - ESpellDictionary *dictionary; + ESpellDictionary *dictionary = NULL; GtkActionGroup *action_group; GtkUIManager *manager; GList *list, *link; @@ -224,14 +221,13 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor, gint ii = 0; guint merge_id; - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - spell_checker = e_html_editor_view_get_spell_checker (view); - - word = e_html_editor_selection_get_caret_word (selection); + cnt_editor = e_html_editor_get_content_editor (editor); + word = e_content_editor_get_caret_word (cnt_editor); if (word == NULL || *word == '\0') return; + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); + dictionary = e_spell_checker_ref_dictionary ( spell_checker, language_code); if (dictionary != NULL) { @@ -298,7 +294,7 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor, } g_list_free_full (list, (GDestroyNotify) g_free); - + g_clear_object (&spell_checker); g_free (path); g_free (word); } @@ -306,124 +302,100 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor, void e_html_editor_update_spell_actions (EHTMLEditor *editor) { - ESpellChecker *checker; - EHTMLEditorView *view; + ESpellChecker *spell_checker; + EContentEditor *cnt_editor; guint count; - view = e_html_editor_get_view (editor); - checker = e_html_editor_view_get_spell_checker (view); + cnt_editor = e_html_editor_get_content_editor (editor); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); - count = e_spell_checker_count_active_languages (checker); + count = e_spell_checker_count_active_languages (spell_checker); gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD), count == 1); gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD_MENU), count > 1); gtk_action_set_visible (ACTION (CONTEXT_SPELL_IGNORE), count > 0); - gtk_action_set_visible (ACTION (SPELL_CHECK), count > 0); - gtk_action_set_visible (ACTION (LANGUAGE_MENU), count > 0); + gtk_action_set_sensitive (ACTION (SPELL_CHECK), count > 0); + gtk_action_set_sensitive (ACTION (LANGUAGE_MENU), e_spell_checker_count_available_dicts (spell_checker) > 0); + + g_clear_object (&spell_checker); } static void -html_editor_update_actions (EHTMLEditor *editor, - GdkEventButton *event) +action_set_visible_and_sensitive (GtkAction *action, + gboolean value) { - WebKitWebView *web_view; - WebKitSpellChecker *checker; - WebKitHitTestResult *hit_test; - WebKitHitTestResultContext context; - WebKitDOMNode *node; - EHTMLEditorSelection *selection; - EHTMLEditorView *view; + gtk_action_set_visible (action, value); + gtk_action_set_sensitive (action, value); +} + +static void +html_editor_update_actions (EHTMLEditor *editor) +{ + EContentEditor *cnt_editor; + EContentEditorNodeFlags flags = editor->priv->node_flags; ESpellChecker *spell_checker; GtkUIManager *manager; GtkActionGroup *action_group; GList *list; - gchar **languages; + gchar **languages = NULL; guint ii, n_languages; gboolean visible; guint merge_id; - gint loc, len; - view = e_html_editor_get_view (editor); - selection = e_html_editor_view_get_selection (view); - spell_checker = e_html_editor_view_get_spell_checker (view); + cnt_editor = e_html_editor_get_content_editor (editor); - web_view = WEBKIT_WEB_VIEW (view); - manager = e_html_editor_get_ui_manager (editor); + if (camel_debug ("wex")) + printf ("%s: flags:%d(%x)\n", G_STRFUNC, flags, flags); - g_clear_object (&editor->priv->table_cell); - g_clear_object (&editor->priv->image); - g_clear_object (&editor->priv->current_node); - - /* Update context menu item visibility. */ - hit_test = webkit_web_view_get_hit_test_result (web_view, event); - g_object_get ( - G_OBJECT (hit_test), - "context", &context, - "inner-node", &node, NULL); - g_object_unref (hit_test); - - editor->priv->current_node = g_object_ref (node); - visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE); - gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_IMAGE), visible); - if (visible) - editor->priv->image = g_object_ref (node); + visible = (flags & E_CONTENT_EDITOR_NODE_IS_IMAGE); + action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_IMAGE), visible); - visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK); - if (visible) { - g_object_unref (editor->priv->current_node); - editor->priv->current_node = webkit_dom_node_get_parent_node (node); - gtk_action_set_visible (ACTION (CONTEXT_INSERT_LINK), FALSE); - } - gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_LINK), visible); + visible = (flags & E_CONTENT_EDITOR_NODE_IS_ANCHOR); + if (visible) + action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_LINK), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_LINK), visible); - visible = (WEBKIT_DOM_IS_HTMLHR_ELEMENT (node)); - gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_RULE), visible); + visible = (flags & E_CONTENT_EDITOR_NODE_IS_H_RULE); + action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_RULE), visible); - visible = (WEBKIT_DOM_IS_TEXT (node)); + visible = (flags & E_CONTENT_EDITOR_NODE_IS_TEXT); /* Only display the text properties dialog when some text is selected. */ - gtk_action_set_visible ( + action_set_visible_and_sensitive ( ACTION (CONTEXT_PROPERTIES_TEXT), - visible && !e_html_editor_selection_is_collapsed (selection)); + visible && !(flags & E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED)); visible = gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_IMAGE)) || gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_LINK)) || visible; /* text node under caret */ - gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_PARAGRAPH), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_PARAGRAPH), visible); /* Set to visible if any of these are true: * - Selection is active and contains a link. * - Cursor is on a link. * - Cursor is on an image that has a URL or target. */ - visible = (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) || - (e_html_editor_dom_node_find_parent_element (node, "A") != NULL)); - gtk_action_set_visible (ACTION (CONTEXT_REMOVE_LINK), visible); - - visible = (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node) || - (e_html_editor_dom_node_find_parent_element (node, "TD") != NULL) || - (e_html_editor_dom_node_find_parent_element (node, "TH") != NULL)); - gtk_action_set_visible (ACTION (CONTEXT_DELETE_CELL), visible); - gtk_action_set_visible (ACTION (CONTEXT_DELETE_COLUMN), visible); - gtk_action_set_visible (ACTION (CONTEXT_DELETE_ROW), visible); - gtk_action_set_visible (ACTION (CONTEXT_DELETE_TABLE), visible); - gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_AFTER), visible); - gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_BEFORE), visible); - gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_ABOVE), visible); - gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_BELOW), visible); - gtk_action_set_visible (ACTION (CONTEXT_INSERT_TABLE), visible); - gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_CELL), visible); - if (visible) - editor->priv->table_cell = g_object_ref (node); - - /* Note the |= (cursor must be in a table cell). */ - visible |= (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (node) || - (e_html_editor_dom_node_find_parent_element (node, "TABLE") != NULL)); - gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TABLE), visible); + visible = (flags & E_CONTENT_EDITOR_NODE_IS_ANCHOR); + action_set_visible_and_sensitive (ACTION (CONTEXT_REMOVE_LINK), visible); + + visible = (flags & E_CONTENT_EDITOR_NODE_IS_TABLE_CELL); + action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_CELL), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_COLUMN), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_ROW), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_TABLE), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_COLUMN_AFTER), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_COLUMN_BEFORE), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_ROW_ABOVE), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_ROW_BELOW), visible); + action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_CELL), visible); + + visible = (flags & E_CONTENT_EDITOR_NODE_IS_TABLE); + action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_TABLE), visible); /********************** Spell Check Suggestions **********************/ + manager = e_html_editor_get_ui_manager (editor); action_group = editor->priv->suggestion_actions; /* Remove the old content from the context menu. */ @@ -442,18 +414,16 @@ html_editor_update_actions (EHTMLEditor *editor, list = g_list_delete_link (list, list); } + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); languages = e_spell_checker_list_active_languages ( spell_checker, &n_languages); /* Decide if we should show spell checking items. */ - checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ()); visible = FALSE; if (n_languages > 0) { - gchar *word = e_html_editor_selection_get_caret_word (selection); + gchar *word = e_content_editor_get_caret_word (cnt_editor); if (word && *word) { - webkit_spell_checker_check_spelling_of_string ( - checker, word, &loc, &len); - visible = (loc > -1); + visible = !e_spell_checker_check_word (spell_checker, word, -1); } else { visible = FALSE; } @@ -463,6 +433,8 @@ html_editor_update_actions (EHTMLEditor *editor, action_group = editor->priv->spell_check_actions; gtk_action_group_set_visible (action_group, visible); + g_clear_object (&spell_checker); + /* Exit early if spell checking items are invisible. */ if (!visible) { g_strfreev (languages); @@ -493,64 +465,60 @@ html_editor_update_actions (EHTMLEditor *editor, static void html_editor_spell_languages_changed (EHTMLEditor *editor) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; ESpellChecker *spell_checker; - WebKitWebSettings *settings; - gchar *comma_separated; gchar **languages; - view = e_html_editor_get_view (editor); - spell_checker = e_html_editor_view_get_spell_checker (view); + cnt_editor = e_html_editor_get_content_editor (editor); + spell_checker = e_content_editor_ref_spell_checker (cnt_editor); languages = e_spell_checker_list_active_languages (spell_checker, NULL); - comma_separated = g_strjoinv (",", languages); - g_strfreev (languages); /* Set the languages for webview to highlight misspelled words */ - settings = webkit_web_view_get_settings ( - WEBKIT_WEB_VIEW (editor->priv->html_editor_view)); - - g_object_set ( - G_OBJECT (settings), - "spell-checking-languages", comma_separated, - NULL); + e_content_editor_set_spell_checking_languages (cnt_editor, (const gchar **) languages); if (editor->priv->spell_check_dialog != NULL) e_html_editor_spell_check_dialog_update_dictionaries ( E_HTML_EDITOR_SPELL_CHECK_DIALOG ( editor->priv->spell_check_dialog)); - if (*comma_separated) - e_html_editor_view_force_spell_check (editor->priv->html_editor_view); - else - e_html_editor_view_turn_spell_check_off (editor->priv->html_editor_view); + e_content_editor_set_spell_check_enabled (cnt_editor, languages && *languages); - g_free (comma_separated); + g_clear_object (&spell_checker); + g_strfreev (languages); } static gboolean -html_editor_show_popup (EHTMLEditor *editor, - GdkEventButton *event, - gpointer user_data) +html_editor_context_menu_requested_cb (EContentEditor *cnt_editor, + EContentEditorNodeFlags flags, + GdkEvent *event, + EHTMLEditor *editor) { GtkWidget *menu; + /* COUNT FLAGS */ menu = e_html_editor_get_managed_widget (editor, "/context-menu"); - g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, event); + editor->priv->node_flags = flags; + g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, flags); if (!gtk_menu_get_attach_widget (GTK_MENU (menu))) gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (editor), NULL); - if (event != NULL) + + if (event) gtk_menu_popup ( GTK_MENU (menu), NULL, NULL, NULL, - user_data, event->button, event->time); + GTK_WIDGET (cnt_editor), + ((GdkEventButton*) event)->button, + ((GdkEventButton*) event)->time); else gtk_menu_popup ( GTK_MENU (menu), NULL, NULL, NULL, - user_data, 0, gtk_get_current_event_time ()); + GTK_WIDGET (cnt_editor), + 0, + gtk_get_current_event_time ()); return TRUE; } @@ -635,7 +603,6 @@ html_editor_constructed (GObject *object) { EHTMLEditor *editor = E_HTML_EDITOR (object); EHTMLEditorPrivate *priv = editor->priv; - GtkIMMulticontext *im_context; GtkWidget *widget; GtkToolbar *toolbar; GtkToolItem *tool_item; @@ -643,6 +610,17 @@ html_editor_constructed (GObject *object) /* Chain up to parent's method. */ G_OBJECT_CLASS (e_html_editor_parent_class)->constructed (object); + e_extensible_load_extensions (E_EXTENSIBLE (object)); + + editor_actions_init (editor); + priv->editor_layout_row = 2; + + /* Tweak the main-toolbar style. */ + widget = e_html_editor_get_managed_widget (editor, "/main-toolbar"); + gtk_style_context_add_class ( + gtk_widget_get_style_context (widget), + GTK_STYLE_CLASS_PRIMARY_TOOLBAR); + /* Construct the editing toolbars. */ widget = e_html_editor_get_managed_widget (editor, "/edit-toolbar"); @@ -675,25 +653,29 @@ html_editor_constructed (GObject *object) /* EAlertBar controls its own visibility. */ /* Construct the main editing area. */ + widget = GTK_WIDGET (e_html_editor_get_content_editor (editor)); - widget = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy ( - GTK_SCROLLED_WINDOW (widget), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type ( - GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); - gtk_widget_set_hexpand (widget, TRUE); - gtk_widget_set_vexpand (widget, TRUE); - gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1); - priv->scrolled_window = g_object_ref (widget); - gtk_widget_show (widget); + /* Pack editors which implement GtkScrollable in a scrolled window */ + if (GTK_IS_SCROLLABLE (widget)) { + GtkWidget *scrolled_window; + + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_widget_show (scrolled_window); + + gtk_grid_attach (GTK_GRID (editor), scrolled_window, 0, 4, 1, 1); + + gtk_container_add (GTK_CONTAINER (scrolled_window), widget); + } else { + gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1); + } - widget = GTK_WIDGET (e_html_editor_get_view (editor)); - gtk_container_add (GTK_CONTAINER (priv->scrolled_window), widget); gtk_widget_show (widget); - g_signal_connect_swapped ( - widget, "popup-event", - G_CALLBACK (html_editor_show_popup), editor); + + g_signal_connect ( + widget, "context-menu-requested", + G_CALLBACK (html_editor_context_menu_requested_cb), editor); /* Add some combo boxes to the "edit" toolbar. */ @@ -734,14 +716,6 @@ html_editor_constructed (GObject *object) gtk_toolbar_insert (toolbar, tool_item, 0); priv->color_combo_box = g_object_ref (widget); gtk_widget_show_all (GTK_WIDGET (tool_item)); - e_binding_bind_property ( - priv->color_combo_box, "current-color", - priv->selection, "font-color", - G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); - e_binding_bind_property ( - priv->html_editor_view, "editable", - priv->color_combo_box, "sensitive", - G_BINDING_SYNC_CREATE); tool_item = gtk_tool_item_new (); widget = e_action_combo_box_new_with_action ( @@ -752,16 +726,6 @@ html_editor_constructed (GObject *object) gtk_toolbar_insert (toolbar, tool_item, 0); priv->size_combo_box = g_object_ref (widget); gtk_widget_show_all (GTK_WIDGET (tool_item)); - - /* Add input methods to the context menu. */ - widget = e_html_editor_get_managed_widget ( - editor, "/context-menu/context-input-methods-menu"); - widget = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); - g_object_get ( - G_OBJECT (priv->html_editor_view), "im-context", &im_context, NULL); - gtk_im_multicontext_append_menuitems ( - GTK_IM_MULTICONTEXT (im_context), - GTK_MENU_SHELL (widget)); } static void @@ -793,19 +757,23 @@ html_editor_dispose (GObject *object) g_clear_object (&priv->mode_combo_box); g_clear_object (&priv->size_combo_box); g_clear_object (&priv->style_combo_box); - g_clear_object (&priv->scrolled_window); - - g_clear_object (&priv->table_cell); - g_clear_object (&priv->current_node); - g_clear_object (&priv->image); - - g_clear_object (&priv->html_editor_view); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_html_editor_parent_class)->dispose (object); } static void +html_editor_finalize (GObject *object) +{ + EHTMLEditor *editor = E_HTML_EDITOR (object); + + g_hash_table_destroy (editor->priv->content_editors); + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_html_editor_parent_class)->finalize (object); +} + +static void html_editor_submit_alert (EAlertSink *alert_sink, EAlert *alert) { @@ -851,6 +819,7 @@ e_html_editor_class_init (EHTMLEditorClass *class) object_class->get_property = html_editor_get_property; object_class->constructed = html_editor_constructed; object_class->dispose = html_editor_dispose; + object_class->finalize = html_editor_finalize; widget_class = GTK_WIDGET_CLASS (class); widget_class->parent_set = html_editor_parent_changed; @@ -877,7 +846,7 @@ e_html_editor_class_init (EHTMLEditorClass *class) NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, - GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + G_TYPE_UINT); signals[SPELL_LANGUAGES_CHANGED] = g_signal_new ( "spell-languages-changed", @@ -899,7 +868,6 @@ static void e_html_editor_init (EHTMLEditor *editor) { EHTMLEditorPrivate *priv; - GtkWidget *widget; gchar *filename; GError *error = NULL; @@ -916,8 +884,7 @@ e_html_editor_init (EHTMLEditor *editor) priv->language_actions = gtk_action_group_new ("language"); priv->spell_check_actions = gtk_action_group_new ("spell-check"); priv->suggestion_actions = gtk_action_group_new ("suggestion"); - priv->html_editor_view = g_object_ref_sink (e_html_editor_view_new ()); - priv->selection = e_html_editor_view_get_selection (priv->html_editor_view); + priv->content_editors = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL); filename = html_editor_find_ui_file ("e-html-editor-manager.ui"); if (!gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error)) { @@ -925,42 +892,186 @@ e_html_editor_init (EHTMLEditor *editor) g_clear_error (&error); } g_free (filename); +} - editor_actions_init (editor); - priv->editor_layout_row = 2; +static void +e_html_editor_content_editor_initialized (EContentEditor *content_editor, + gpointer user_data) +{ + ESimpleAsyncResult *async_result = user_data; + EHTMLEditor *html_editor; - /* Tweak the main-toolbar style. */ - widget = e_html_editor_get_managed_widget (editor, "/main-toolbar"); - gtk_style_context_add_class ( - gtk_widget_get_style_context (widget), - GTK_STYLE_CLASS_PRIMARY_TOOLBAR); + g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (async_result)); + + html_editor = e_simple_async_result_get_user_data (async_result); + g_return_if_fail (E_IS_HTML_EDITOR (html_editor)); + g_return_if_fail (content_editor == e_html_editor_get_content_editor (html_editor)); + + e_binding_bind_property ( + html_editor->priv->color_combo_box, "current-color", + content_editor, "font-color", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + e_binding_bind_property ( + content_editor, "editable", + html_editor->priv->color_combo_box, "sensitive", + G_BINDING_SYNC_CREATE); + editor_actions_bind (html_editor); + + g_object_set (G_OBJECT (content_editor), + "halign", GTK_ALIGN_FILL, + "hexpand", TRUE, + "valign", GTK_ALIGN_FILL, + "vexpand", TRUE, + "changed", FALSE, + NULL); + + e_simple_async_result_complete (async_result); + + g_object_unref (async_result); } /** * e_html_editor_new: + * @callback: a callback to be called when the editor is ready + * @user_data: a used data passed into the @callback * - * Constructs a new #EHTMLEditor. + * Constructs a new #EHTMLEditor asynchronously. The result is returned + * by e_html_editor_new_finish(), which should be called inside @callback. * - * Returns: A newly created widget. [transfer-full] - */ + * Since: 3.22 + **/ +void +e_html_editor_new (GAsyncReadyCallback callback, + gpointer user_data) +{ + EHTMLEditor *html_editor; + EContentEditor *content_editor; + ESimpleAsyncResult *async_result; + + g_return_if_fail (callback != NULL); + + html_editor = g_object_new (E_TYPE_HTML_EDITOR, NULL); + async_result = e_simple_async_result_new (NULL, callback, user_data, e_html_editor_new); + e_simple_async_result_set_user_data (async_result, html_editor, g_object_unref); + + content_editor = e_html_editor_get_content_editor (html_editor); + e_content_editor_initialize (content_editor, e_html_editor_content_editor_initialized, async_result); +} + +/** + * e_html_editor_new_finish: + * @result: a #GAsyncResult passed to callback from e_html_editor_new() + * @error: an optional #GError + * + * Finishes the call of e_html_editor_new(). + * + * Returns: (transfer-full): A newly created #EHTMLEditor. + * + * Since: 3.22 + **/ GtkWidget * -e_html_editor_new (void) +e_html_editor_new_finish (GAsyncResult *result, + GError **error) { - return g_object_new (E_TYPE_HTML_EDITOR, NULL); + ESimpleAsyncResult *eresult; + + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, e_html_editor_new), NULL); + + eresult = E_SIMPLE_ASYNC_RESULT (result); + + return e_simple_async_result_steal_user_data (eresult); } /** - * e_html_editor_get_view: + * e_html_editor_get_content_editor: * @editor: an #EHTMLEditor * - * Returns instance of #EHTMLEditorView used in the @editor. + * Returns instance of #EContentEditor used in the @editor. */ -EHTMLEditorView * -e_html_editor_get_view (EHTMLEditor *editor) +EContentEditor * +e_html_editor_get_content_editor (EHTMLEditor *editor) { g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL); - return editor->priv->html_editor_view; + if (!editor->priv->use_content_editor) { + GSettings *settings; + gchar *name; + + if (!g_hash_table_size (editor->priv->content_editors)) + return NULL; + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + name = g_settings_get_string (settings, "composer-editor"); + g_clear_object (&settings); + + if (name) + editor->priv->use_content_editor = g_hash_table_lookup (editor->priv->content_editors, name); + + g_free (name); + + if (!editor->priv->use_content_editor) + editor->priv->use_content_editor = g_hash_table_lookup (editor->priv->content_editors, DEFAULT_CONTENT_EDITOR_NAME); + + if (!editor->priv->use_content_editor) { + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, editor->priv->content_editors); + if (g_hash_table_iter_next (&iter, &key, &value)) { + editor->priv->use_content_editor = value; + } + } + + if (editor->priv->use_content_editor) + e_content_editor_setup_editor (editor->priv->use_content_editor, editor); + } + + return editor->priv->use_content_editor; +} + +/* Private function */ +const gchar * +e_html_editor_get_content_editor_name (EHTMLEditor *editor) +{ + EContentEditor *cnt_editor; + GHashTableIter iter; + gpointer key, value; + + g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL); + + cnt_editor = e_html_editor_get_content_editor (editor); + if (!cnt_editor) + return NULL; + + g_hash_table_iter_init (&iter, editor->priv->content_editors); + if (g_hash_table_iter_next (&iter, &key, &value)) { + if (value == cnt_editor) + return key; + } + + return NULL; +} + +void +e_html_editor_register_content_editor (EHTMLEditor *editor, + const gchar *name, + EContentEditor *cnt_editor) +{ + EContentEditor *already_taken; + + g_return_if_fail (E_IS_HTML_EDITOR (editor)); + g_return_if_fail (name != NULL); + g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor)); + + already_taken = g_hash_table_lookup (editor->priv->content_editors, name); + + if (already_taken) { + g_warning ("%s: Cannot register %s with name '%s', because it's already taken by %s", + G_STRFUNC, G_OBJECT_TYPE_NAME (cnt_editor), name, G_OBJECT_TYPE_NAME (already_taken)); + } else { + g_hash_table_insert (editor->priv->content_editors, g_strdup (name), cnt_editor); + } } /** @@ -1175,7 +1286,7 @@ e_html_editor_pack_above (EHTMLEditor *editor, * @as_html: whether the content should be saved as HTML or plain text * @error:[out] a #GError * - * Saves current content of the #EHTMLEditorView into given file. When @as_html + * Saves current content of the #EContentEditor into given file. When @as_html * is @FALSE, the content is first converted into plain text. * * Returns: @TRUE when content is succesfully saved, @FALSE otherwise. @@ -1186,6 +1297,7 @@ e_html_editor_save (EHTMLEditor *editor, gboolean as_html, GError **error) { + EContentEditor *cnt_editor; GFile *file; GFileOutputStream *stream; gchar *content; @@ -1197,12 +1309,20 @@ e_html_editor_save (EHTMLEditor *editor, if ((error && *error) || !stream) return FALSE; + cnt_editor = e_html_editor_get_content_editor (editor); + if (as_html) - content = e_html_editor_view_get_text_html ( - E_HTML_EDITOR_VIEW (editor), NULL, NULL); + content = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_HTML | + E_CONTENT_EDITOR_GET_PROCESSED, + NULL, NULL); else - content = e_html_editor_view_get_text_plain ( - E_HTML_EDITOR_VIEW (editor)); + content = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_PLAIN | + E_CONTENT_EDITOR_GET_PROCESSED, + NULL, NULL); if (!content || !*content) { g_set_error ( diff --git a/e-util/e-html-editor.h b/e-util/e-html-editor.h index c78d33d..e1381da 100644 --- a/e-util/e-html-editor.h +++ b/e-util/e-html-editor.h @@ -28,7 +28,7 @@ #include <gtk/gtk.h> #include <e-util/e-activity.h> #include <e-util/e-activity-bar.h> -#include <e-util/e-html-editor-view.h> +#include <e-util/e-content-editor.h> /* Standard GObject macros */ #define E_TYPE_HTML_EDITOR \ @@ -63,16 +63,24 @@ struct _EHTMLEditor { struct _EHTMLEditorClass { GtkGridClass parent_class; - void (*update_actions) (EHTMLEditor *editor, - GdkEventButton *event); + void (*update_actions) (EHTMLEditor *editor); + void (*spell_languages_changed) (EHTMLEditor *editor); }; GType e_html_editor_get_type (void) G_GNUC_CONST; -GtkWidget * e_html_editor_new (void); -EHTMLEditorView * - e_html_editor_get_view (EHTMLEditor *editor); +void e_html_editor_new (GAsyncReadyCallback callback, + gpointer user_data); +GtkWidget * e_html_editor_new_finish (GAsyncResult *result, + GError **error); +EContentEditor * + e_html_editor_get_content_editor + (EHTMLEditor *editor); +void e_html_editor_register_content_editor + (EHTMLEditor *editor, + const gchar *name, + EContentEditor *cnt_editor); GtkBuilder * e_html_editor_get_builder (EHTMLEditor *editor); GtkUIManager * e_html_editor_get_ui_manager (EHTMLEditor *editor); GtkAction * e_html_editor_get_action (EHTMLEditor *editor, diff --git a/e-util/e-mail-signature-editor.c b/e-util/e-mail-signature-editor.c index ae67296..45c5f7a 100644 --- a/e-util/e-mail-signature-editor.c +++ b/e-util/e-mail-signature-editor.c @@ -24,6 +24,7 @@ #include "e-alert-dialog.h" #include "e-alert-sink.h" #include "e-alert-bar.h" +#include "e-simple-async-result.h" #define E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -101,7 +102,7 @@ mail_signature_editor_loaded_cb (GObject *object, gpointer user_data) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; ESource *source; EMailSignatureEditor *window; ESourceMailSignature *extension; @@ -144,17 +145,24 @@ mail_signature_editor_loaded_cb (GObject *object, is_html = (g_strcmp0 (mime_type, "text/html") == 0); editor = e_mail_signature_editor_get_editor (window); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_html_mode (view, is_html); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_html_mode (cnt_editor, is_html); if (is_html) { - if (strstr (contents, "data-evo-signature-plain-text-mode")) { - e_html_editor_view_set_html_mode (view, FALSE); - e_html_editor_view_set_is_message_from_draft (view, TRUE); - } - e_html_editor_view_set_text_html (view, contents); + if (strstr (contents, "data-evo-signature-plain-text-mode")) + e_content_editor_set_html_mode (cnt_editor, FALSE); + + e_content_editor_insert_content ( + cnt_editor, + contents, + E_CONTENT_EDITOR_INSERT_TEXT_HTML | + E_CONTENT_EDITOR_INSERT_REPLACE_ALL); } else - e_html_editor_view_set_text_plain (view, contents); + e_content_editor_insert_content ( + cnt_editor, + contents, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN | + E_CONTENT_EDITOR_INSERT_REPLACE_ALL); g_free (contents); @@ -180,18 +188,18 @@ action_close_cb (GtkAction *action, EMailSignatureEditor *window) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; gboolean something_changed = FALSE; const gchar *original_name; const gchar *signature_name; + editor = e_mail_signature_editor_get_editor (window); + cnt_editor = e_html_editor_get_content_editor (editor); + original_name = window->priv->original_name; signature_name = gtk_entry_get_text (GTK_ENTRY (window->priv->entry)); - editor = e_mail_signature_editor_get_editor (window); - view = e_html_editor_get_view (editor); - - something_changed |= e_html_editor_view_can_undo (view); + something_changed |= e_content_editor_can_undo (cnt_editor); something_changed |= (strcmp (signature_name, original_name) != 0); if (something_changed) { @@ -202,6 +210,7 @@ action_close_cb (GtkAction *action, "widgets:ask-signature-changed", NULL); if (response == GTK_RESPONSE_YES) { GtkActionGroup *action_group; + GtkAction *action; action_group = window->priv->action_group; action = gtk_action_group_get_action ( @@ -317,6 +326,16 @@ static GtkActionEntry entries[] = { }; static void +mail_signature_editor_set_editor (EMailSignatureEditor *editor, + EHTMLEditor *html_editor) +{ + g_return_if_fail (E_IS_HTML_EDITOR (html_editor)); + g_return_if_fail (editor->priv->editor == NULL); + + editor->priv->editor = g_object_ref (html_editor); +} + +static void mail_signature_editor_set_registry (EMailSignatureEditor *editor, ESourceRegistry *registry) { @@ -366,6 +385,12 @@ mail_signature_editor_set_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_EDITOR: + mail_signature_editor_set_editor ( + E_MAIL_SIGNATURE_EDITOR (object), + g_value_get_object (value)); + return; + case PROP_REGISTRY: mail_signature_editor_set_registry ( E_MAIL_SIGNATURE_EDITOR (object), @@ -485,7 +510,7 @@ mail_signature_editor_constructed (GObject *object) GtkActionGroup *action_group; EFocusTracker *focus_tracker; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GtkUIManager *ui_manager; GDBusObject *dbus_object; ESource *source; @@ -501,7 +526,7 @@ mail_signature_editor_constructed (GObject *object) window = E_MAIL_SIGNATURE_EDITOR (object); editor = e_mail_signature_editor_get_editor (window); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); ui_manager = e_html_editor_get_ui_manager (editor); @@ -612,7 +637,7 @@ mail_signature_editor_constructed (GObject *object) if (source == NULL) { gtk_widget_grab_focus (window->priv->entry); } else { - gtk_widget_grab_focus (GTK_WIDGET (view)); + gtk_widget_grab_focus (GTK_WIDGET (cnt_editor)); } /* Load file content only for an existing signature. @@ -659,7 +684,8 @@ e_mail_signature_editor_class_init (EMailSignatureEditorClass *class) NULL, NULL, E_TYPE_HTML_EDITOR, - G_PARAM_READABLE | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( @@ -702,23 +728,95 @@ static void e_mail_signature_editor_init (EMailSignatureEditor *editor) { editor->priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (editor); +} - editor->priv->editor = g_object_ref_sink (e_html_editor_new ()); +typedef struct _CreateEditorData { + ESourceRegistry *registry; + ESource *source; +} CreateEditorData; + +static void +create_editor_data_free (gpointer ptr) +{ + CreateEditorData *ced = ptr; + + if (ced) { + g_clear_object (&ced->registry); + g_clear_object (&ced->source); + g_free (ced); + } } -GtkWidget * +static void +mail_signature_editor_html_editor_created_cb (GObject *source_object, + GAsyncResult *async_result, + gpointer user_data) +{ + GtkWidget *html_editor, *signature_editor; + ESimpleAsyncResult *eresult = user_data; + CreateEditorData *ced; + GError *error = NULL; + + g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (eresult)); + + ced = e_simple_async_result_get_user_data (eresult); + g_return_if_fail (ced != NULL); + + html_editor = e_html_editor_new_finish (async_result, &error); + if (error) { + g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } + + signature_editor = g_object_new (E_TYPE_MAIL_SIGNATURE_EDITOR, + "registry", ced->registry, + "source", ced->source, + "editor", html_editor, + NULL); + + e_simple_async_result_set_op_pointer (eresult, signature_editor); + + e_simple_async_result_complete (eresult); + + g_object_unref (eresult); +} + +void e_mail_signature_editor_new (ESourceRegistry *registry, - ESource *source) + ESource *source, + GAsyncReadyCallback callback, + gpointer user_data) { - g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + ESimpleAsyncResult *eresult; + CreateEditorData *ced; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); if (source != NULL) - g_return_val_if_fail (E_IS_SOURCE (source), NULL); + g_return_if_fail (E_IS_SOURCE (source)); + + ced = g_new0 (CreateEditorData, 1); + ced->registry = g_object_ref (registry); + ced->source = source ? g_object_ref (source) : NULL; + + eresult = e_simple_async_result_new (NULL, callback, user_data, e_mail_signature_editor_new); + e_simple_async_result_set_user_data (eresult, ced, create_editor_data_free); + + e_html_editor_new (mail_signature_editor_html_editor_created_cb, eresult); +} + +GtkWidget * +e_mail_signature_editor_new_finish (GAsyncResult *result, + GError **error) +{ + ESimpleAsyncResult *eresult; + + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, e_mail_signature_editor_new), NULL); + + eresult = E_SIMPLE_ASYNC_RESULT (result); - return g_object_new ( - E_TYPE_MAIL_SIGNATURE_EDITOR, - "registry", registry, - "source", source, NULL); + return e_simple_async_result_get_op_pointer (eresult); } EHTMLEditor * @@ -825,7 +923,7 @@ e_mail_signature_editor_commit (EMailSignatureEditor *window, const gchar *mime_type; gchar *contents; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (window)); @@ -833,10 +931,14 @@ e_mail_signature_editor_commit (EMailSignatureEditor *window, source = e_mail_signature_editor_get_source (window); editor = e_mail_signature_editor_get_editor (window); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); mime_type = "text/html"; - contents = e_html_editor_view_get_body_text_html_for_drafts (view); + contents = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_HTML | + E_CONTENT_EDITOR_GET_BODY, + NULL, NULL); extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; extension = e_source_get_extension (source, extension_name); diff --git a/e-util/e-mail-signature-editor.h b/e-util/e-mail-signature-editor.h index 1b8622d..5cb4b72 100644 --- a/e-util/e-mail-signature-editor.h +++ b/e-util/e-mail-signature-editor.h @@ -63,8 +63,13 @@ struct _EMailSignatureEditorClass { GType e_mail_signature_editor_get_type (void) G_GNUC_CONST; -GtkWidget * e_mail_signature_editor_new (ESourceRegistry *registry, - ESource *source); +void e_mail_signature_editor_new (ESourceRegistry *registry, + ESource *source, + GAsyncReadyCallback callback, + gpointer user_data); +GtkWidget * e_mail_signature_editor_new_finish + (GAsyncResult *result, + GError **error); EHTMLEditor * e_mail_signature_editor_get_editor (EMailSignatureEditor *editor); EFocusTracker * e_mail_signature_editor_get_focus_tracker diff --git a/e-util/e-mail-signature-manager.c b/e-util/e-mail-signature-manager.c index 1ebb9a5..e202039 100644 --- a/e-util/e-mail-signature-manager.c +++ b/e-util/e-mail-signature-manager.c @@ -44,6 +44,7 @@ struct _EMailSignatureManagerPrivate { GtkWidget *edit_button; /* not referenced */ GtkWidget *remove_button; /* not referenced */ GtkWidget *preview; /* not referenced */ + GtkWidget *preview_frame; /* not referenced */ gboolean prefer_html; }; @@ -376,13 +377,10 @@ mail_signature_manager_constructed (GObject *object) container = GTK_WIDGET (manager); - widget = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy ( - GTK_SCROLLED_WINDOW (widget), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type ( - GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + widget = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN); gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE); + manager->priv->preview_frame = widget; /* not referenced */ gtk_widget_show (widget); container = widget; @@ -396,26 +394,46 @@ mail_signature_manager_constructed (GObject *object) } static void -mail_signature_manager_add_signature (EMailSignatureManager *manager) +mail_signature_manager_editor_created_add_signature_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { + EMailSignatureManager *manager = user_data; EHTMLEditor *editor; - EHTMLEditorView *view; - ESourceRegistry *registry; + EContentEditor *cnt_editor; GtkWidget *widget; + GError *error = NULL; - registry = e_mail_signature_manager_get_registry (manager); + g_return_if_fail (E_IS_MAIL_SIGNATURE_MANAGER (manager)); - widget = e_mail_signature_editor_new (registry, NULL); + widget = e_mail_signature_editor_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create signature editor: %s", G_STRFUNC, error->message); + g_clear_error (&error); + g_clear_object (&manager); + return; + } - editor = e_mail_signature_editor_get_editor ( - E_MAIL_SIGNATURE_EDITOR (widget)); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_html_mode ( - view, manager->priv->prefer_html); + editor = e_mail_signature_editor_get_editor (E_MAIL_SIGNATURE_EDITOR (widget)); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_html_mode (cnt_editor, manager->priv->prefer_html); mail_signature_manager_emit_editor_created (manager, widget); gtk_widget_grab_focus (manager->priv->tree_view); + + g_clear_object (&manager); +} + +static void +mail_signature_manager_add_signature (EMailSignatureManager *manager) +{ + ESourceRegistry *registry; + + registry = e_mail_signature_manager_get_registry (manager); + + e_mail_signature_editor_new (registry, NULL, + mail_signature_manager_editor_created_add_signature_cb, g_object_ref (manager)); } static void @@ -447,12 +465,35 @@ mail_signature_manager_editor_created (EMailSignatureManager *manager, } static void +mail_signature_manager_editor_created_edit_signature_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + EMailSignatureManager *manager = user_data; + GtkWidget *widget; + GError *error = NULL; + + g_return_if_fail (E_IS_MAIL_SIGNATURE_MANAGER (manager)); + + widget = e_mail_signature_editor_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create signature editor: %s", G_STRFUNC, error->message); + g_clear_error (&error); + g_clear_object (&manager); + return; + } + + mail_signature_manager_emit_editor_created (manager, widget); + + g_clear_object (&manager); +} + +static void mail_signature_manager_edit_signature (EMailSignatureManager *manager) { EMailSignatureTreeView *tree_view; ESourceMailSignature *extension; ESourceRegistry *registry; - GtkWidget *editor; ESource *source; GFileInfo *file_info; GFile *file; @@ -488,8 +529,8 @@ mail_signature_manager_edit_signature (EMailSignatureManager *manager) if (g_file_info_get_attribute_boolean (file_info, attribute)) goto script; - editor = e_mail_signature_editor_new (registry, source); - mail_signature_manager_emit_editor_created (manager, editor); + e_mail_signature_editor_new (registry, source, + mail_signature_manager_editor_created_edit_signature_cb, g_object_ref (manager)); goto exit; diff --git a/e-util/e-mail-signature-preview.c b/e-util/e-mail-signature-preview.c index 32acfb0..d8e4553 100644 --- a/e-util/e-mail-signature-preview.c +++ b/e-util/e-mail-signature-preview.c @@ -56,78 +56,6 @@ G_DEFINE_TYPE ( E_TYPE_WEB_VIEW) static void -replace_local_image_links (WebKitDOMDocument *document) -{ - gint ii, length; - WebKitDOMNodeList *list; - - list = webkit_dom_document_query_selector_all ( - document, "img[src^=\"file://\"]", NULL); - length = webkit_dom_node_list_get_length (list); - - for (ii = 0; ii < length; ii++) { - gchar *src, *new_src; - WebKitDOMHTMLImageElement *img; - - img = WEBKIT_DOM_HTML_IMAGE_ELEMENT ( - webkit_dom_node_list_item (list, ii)); - src = webkit_dom_html_image_element_get_src (img); - - /* this forms "evo-file://", which can be loaded, - * while "file://" cannot be, due to WebKit policy */ - new_src = g_strconcat ("evo-", src, NULL); - webkit_dom_html_image_element_set_src (img, new_src); - g_free (new_src); - g_free (src); - g_object_unref (img); - } - g_object_unref (list); - - list = webkit_dom_document_get_elements_by_tag_name ( document, "iframe"); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMDocument *content_document; - WebKitDOMHTMLIFrameElement *iframe; - - iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT ( - webkit_dom_node_list_item (list, ii)); - - content_document = - webkit_dom_html_iframe_element_get_content_document (iframe); - - if (content_document && WEBKIT_DOM_IS_DOCUMENT (content_document)) - replace_local_image_links (content_document); - g_object_unref (iframe); - } - g_object_unref (list); -} - -static void -signature_preview_document_loaded_cb (WebKitWebView *web_view, - WebKitWebFrame *web_frame, - gpointer user_data) -{ - WebKitDOMDocument *document; - - document = webkit_web_view_get_dom_document (web_view); - replace_local_image_links (document); - - if ((webkit_dom_document_query_selector ( - document, "[data-evo-signature-plain-text-mode]", NULL))) { - - WebKitDOMHTMLElement *body; - - body = webkit_dom_document_get_body (document); - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body), - "style", - "font-family: Monospace;", - NULL); - } -} - -static void mail_signature_preview_load_cb (ESource *source, GAsyncResult *result, EMailSignaturePreview *preview) @@ -166,16 +94,14 @@ mail_signature_preview_load_cb (ESource *source, mime_type = e_source_mail_signature_get_mime_type (extension); if (g_strcmp0 (mime_type, "text/html") == 0) { - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (preview), contents, - "text/html", "UTF-8", "file:///"); + webkit_web_view_load_html ( + WEBKIT_WEB_VIEW (preview), contents, "file:///"); } else { gchar *string; string = g_markup_printf_escaped ("<pre>%s</pre>", contents); - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (preview), string, - "text/html", "UTF-8", "file:///"); + webkit_web_view_load_html ( + WEBKIT_WEB_VIEW (preview), string, "file:///"); g_free (string); } @@ -378,10 +304,6 @@ static void e_mail_signature_preview_init (EMailSignaturePreview *preview) { preview->priv = E_MAIL_SIGNATURE_PREVIEW_GET_PRIVATE (preview); - - g_signal_connect ( - preview, "document-load-finished", - G_CALLBACK (signature_preview_document_loaded_cb), NULL); } GtkWidget * diff --git a/e-util/e-marshal.list b/e-util/e-marshal.list index ee2106f..1ccafed 100644 --- a/e-util/e-marshal.list +++ b/e-util/e-marshal.list @@ -1,6 +1,7 @@ BOOLEAN:BOXED BOOLEAN:BOXED,STRING BOOLEAN:INT,INT,BOXED +BOOLEAN:INT,BOXED BOOLEAN:INT,INT,OBJECT,INT,INT,UINT BOOLEAN:INT,POINTER,INT,BOXED BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT diff --git a/e-util/e-misc-utils.c b/e-util/e-misc-utils.c index df04561..3dcc7f0 100644 --- a/e-util/e-misc-utils.c +++ b/e-util/e-misc-utils.c @@ -3202,6 +3202,42 @@ e_util_cleanup_settings (void) g_mutex_unlock (&settings_hash_lock); } +static gdouble +get_screen_dpi (GdkScreen *screen) +{ + gdouble dpi; + gdouble dp, di; + + dpi = gdk_screen_get_resolution (screen); + if (dpi != -1) + return dpi; + + dp = hypot (gdk_screen_get_width (screen), gdk_screen_get_height (screen)); + di = hypot (gdk_screen_get_width_mm (screen), gdk_screen_get_height_mm (screen)) / 25.4; + + return dp / di; +} + +guint +e_util_normalize_font_size (GtkWidget *widget, + gdouble font_size) +{ + /* WebKit2 uses font sizes in pixels. */ + GdkScreen *screen; + gdouble dpi; + + if (widget) { + screen = gtk_widget_has_screen (widget) ? + gtk_widget_get_screen (widget) : gdk_screen_get_default (); + } else { + screen = gdk_screen_get_default (); + } + + dpi = screen ? get_screen_dpi (screen) : 96; + + return font_size / 72.0 * dpi; +} + /** * e_util_prompt_user: * @parent: parent window @@ -3405,6 +3441,22 @@ e_util_set_entry_issue_hint (GtkWidget *entry, } } +static GThread *main_thread = NULL; + +void +e_util_init_main_thread (GThread *thread) +{ + g_return_if_fail (main_thread == NULL); + + main_thread = thread ? thread : g_thread_self (); +} + +gboolean +e_util_is_main_thread (GThread *thread) +{ + return thread ? thread == main_thread : g_thread_self () == main_thread; +} + /** * e_util_save_image_from_clipboard: * @clipboard: a #GtkClipboard @@ -3446,7 +3498,7 @@ e_util_save_image_from_clipboard (GtkClipboard *clipboard) /* Convert the filename to a URI. */ uri = g_filename_to_uri (filename, NULL, &error); -exit: + exit: if (error != NULL) { g_warning ("%s", error->message); g_error_free (error); diff --git a/e-util/e-misc-utils.h b/e-util/e-misc-utils.h index bde194a..9afe95c 100644 --- a/e-util/e-misc-utils.h +++ b/e-util/e-misc-utils.h @@ -287,9 +287,13 @@ void e_util_run_simple_async_result_in_thread GSimpleAsyncThreadFunc func, GCancellable *cancellable); gboolean e_util_is_running_gnome (void); - void e_util_set_entry_issue_hint (GtkWidget *entry, const gchar *hint); + +guint e_util_normalize_font_size (GtkWidget *widget, + gdouble font_size); +void e_util_init_main_thread (GThread *thread); +gboolean e_util_is_main_thread (GThread *thread); gchar * e_util_save_image_from_clipboard (GtkClipboard *clipboard); diff --git a/e-util/e-search-bar.c b/e-util/e-search-bar.c index 759956a..b5ebdde 100644 --- a/e-util/e-search-bar.c +++ b/e-util/e-search-bar.c @@ -44,9 +44,11 @@ struct _ESearchBarPrivate { GtkWidget *prev_button; GtkWidget *next_button; + WebKitFindController *find_controller; + gchar *active_search; - guint rerun_search : 1; + gboolean search_forward; }; enum { @@ -77,7 +79,6 @@ search_bar_update_matches (ESearchBar *search_bar, GtkWidget *matches_label; gchar *text; - search_bar->priv->rerun_search = FALSE; matches_label = search_bar->priv->matches_label; text = g_strdup_printf (_("Matches: %u"), matches); @@ -86,14 +87,88 @@ search_bar_update_matches (ESearchBar *search_bar, g_free (text); } + static void +webkit_find_controller_found_text_cb (WebKitFindController *find_controller, + guint match_count, + ESearchBar *search_bar) +{ + GtkWidget *widget; + WebKitFindOptions options; + gboolean wrapped = FALSE; + + search_bar_update_matches (search_bar, match_count); + + g_free (search_bar->priv->active_search); + search_bar->priv->active_search = + g_strdup (webkit_find_controller_get_search_text (find_controller)); + + gtk_widget_set_sensitive (search_bar->priv->next_button, TRUE); + gtk_widget_set_sensitive (search_bar->priv->prev_button, TRUE); + + g_object_notify (G_OBJECT (search_bar), "active-search"); + + options = webkit_find_controller_get_options (find_controller); + + if (options & WEBKIT_FIND_OPTIONS_WRAP_AROUND) + wrapped = TRUE; + + /* Update wrapped label visibility. */ + widget = search_bar->priv->wrapped_next_box; + + if (wrapped && search_bar->priv->search_forward) + gtk_widget_show (widget); + else + gtk_widget_hide (widget); + + widget = search_bar->priv->wrapped_prev_box; + + if (wrapped && !search_bar->priv->search_forward) + gtk_widget_show (widget); + else + gtk_widget_hide (widget); +} + static void -search_bar_update_highlights (ESearchBar *search_bar) +webkit_find_controller_failed_to_found_text_cb (WebKitFindController *find_controller, + ESearchBar *search_bar) { - EWebView *web_view; + WebKitFindOptions options; + GtkWidget *widget; + + options = webkit_find_controller_get_options (find_controller); + + /* If we didn't find anything, try from the beggining with WRAP_AROUND option */ + if (!(options & WEBKIT_FIND_OPTIONS_WRAP_AROUND)) { + webkit_find_controller_search ( + find_controller, + webkit_find_controller_get_search_text (find_controller), + options | WEBKIT_FIND_OPTIONS_WRAP_AROUND, + G_MAXUINT); + } + + search_bar_update_matches (search_bar, 0); + + g_free (search_bar->priv->active_search); + search_bar->priv->active_search = + g_strdup (webkit_find_controller_get_search_text (find_controller)); - web_view = e_search_bar_get_web_view (search_bar); + gtk_widget_set_sensitive (search_bar->priv->next_button, FALSE); + gtk_widget_set_sensitive (search_bar->priv->prev_button, FALSE); - webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (web_view)); + g_object_notify (G_OBJECT (search_bar), "active-search"); + + /* Update wrapped label visibility. */ + widget = search_bar->priv->wrapped_next_box; + gtk_widget_hide (widget); + + widget = search_bar->priv->wrapped_prev_box; + gtk_widget_hide (widget); +} + +static void +search_bar_update_highlights (ESearchBar *search_bar) +{ + webkit_find_controller_search_finish (search_bar->priv->find_controller); e_search_bar_changed (search_bar); } @@ -102,15 +177,13 @@ static void search_bar_find (ESearchBar *search_bar, gboolean search_forward) { - EWebView *web_view; - GtkWidget *widget; + WebKitFindController *find_controller; gboolean case_sensitive; - gboolean wrapped = FALSE; - gboolean success; gchar *text; - guint matches; - web_view = e_search_bar_get_web_view (search_bar); + find_controller = search_bar->priv->find_controller; + search_bar->priv->search_forward = search_forward; + case_sensitive = e_search_bar_get_case_sensitive (search_bar); text = e_search_bar_get_text (search_bar); @@ -120,46 +193,14 @@ search_bar_find (ESearchBar *search_bar, return; } - webkit_web_view_unmark_text_matches ( - WEBKIT_WEB_VIEW (web_view)); - matches = webkit_web_view_mark_text_matches ( - WEBKIT_WEB_VIEW (web_view), - text, case_sensitive, 0); - webkit_web_view_set_highlight_text_matches ( - WEBKIT_WEB_VIEW (web_view), TRUE); - search_bar_update_matches (search_bar, matches); - - success = webkit_web_view_search_text ( - WEBKIT_WEB_VIEW (web_view), - text, case_sensitive, search_forward, FALSE); - - if (!success) - wrapped = webkit_web_view_search_text ( - WEBKIT_WEB_VIEW (web_view), - text, case_sensitive, search_forward, TRUE); - - g_free (search_bar->priv->active_search); - search_bar->priv->active_search = text; - - gtk_widget_set_sensitive (search_bar->priv->next_button, matches != 0); - gtk_widget_set_sensitive (search_bar->priv->prev_button, matches != 0); - - g_object_notify (G_OBJECT (search_bar), "active-search"); - - /* Update wrapped label visibility. */ - widget = search_bar->priv->wrapped_next_box; - - if (wrapped && search_forward) - gtk_widget_show (widget); - else - gtk_widget_hide (widget); - - widget = search_bar->priv->wrapped_prev_box; + webkit_find_controller_search_finish (find_controller); + webkit_find_controller_search ( + find_controller, + text, + case_sensitive ? WEBKIT_FIND_OPTIONS_NONE : WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE, + G_MAXUINT); - if (wrapped && !search_forward) - gtk_widget_show (widget); - else - gtk_widget_hide (widget); + g_free (text); } static void @@ -205,43 +246,48 @@ search_bar_toggled_cb (ESearchBar *search_bar) } static void -web_view_load_status_changed_cb (WebKitWebView *webkit_web_view, - GParamSpec *pspec, - gpointer user_data) +web_view_load_changed_cb (WebKitWebView *webkit_web_view, + WebKitLoadEvent load_event, + ESearchBar *search_bar) { - WebKitLoadStatus status; - ESearchBar *search_bar; - - status = webkit_web_view_get_load_status (webkit_web_view); - if (status != WEBKIT_LOAD_FINISHED) - return; - - if (!user_data) + if (load_event != WEBKIT_LOAD_FINISHED) return; - search_bar = E_SEARCH_BAR (user_data); - if (gtk_widget_get_visible (GTK_WIDGET (search_bar))) { if (search_bar->priv->active_search != NULL) { search_bar_find (search_bar, TRUE); } - } else { + } else e_web_view_update_highlights (search_bar->priv->web_view); - } } static void search_bar_set_web_view (ESearchBar *search_bar, EWebView *web_view) { + WebKitFindController *find_controller; + g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_return_if_fail (search_bar->priv->web_view == NULL); search_bar->priv->web_view = g_object_ref (web_view); - e_signal_connect_notify ( - web_view, "notify::load-status", - G_CALLBACK (web_view_load_status_changed_cb), search_bar); + find_controller = + webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (web_view)); + + search_bar->priv->find_controller = find_controller; + + g_signal_connect ( + web_view, "load-changed", + G_CALLBACK (web_view_load_changed_cb), search_bar); + + g_signal_connect ( + find_controller, "found-text", + G_CALLBACK (webkit_find_controller_found_text_cb), search_bar); + + g_signal_connect ( + find_controller, "failed-to-find-text", + G_CALLBACK (webkit_find_controller_failed_to_found_text_cb), search_bar); } static void @@ -393,7 +439,6 @@ static void search_bar_show (GtkWidget *widget) { ESearchBar *search_bar; - EWebView *web_view; search_bar = E_SEARCH_BAR (widget); @@ -402,8 +447,7 @@ search_bar_show (GtkWidget *widget) gtk_widget_grab_focus (search_bar->priv->entry); - web_view = e_search_bar_get_web_view (search_bar); - webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (web_view)); + webkit_find_controller_search_finish (search_bar->priv->find_controller); search_bar_find (search_bar, TRUE); } @@ -442,8 +486,6 @@ search_bar_key_press_event (GtkWidget *widget, static void search_bar_clear (ESearchBar *search_bar) { - WebKitWebView *web_view; - g_free (search_bar->priv->active_search); search_bar->priv->active_search = NULL; @@ -455,9 +497,6 @@ search_bar_clear (ESearchBar *search_bar) search_bar_update_highlights (search_bar); - web_view = WEBKIT_WEB_VIEW (search_bar->priv->web_view); - webkit_web_view_unmark_text_matches (web_view); - g_object_notify (G_OBJECT (search_bar), "active-search"); } diff --git a/e-util/e-simple-async-result.c b/e-util/e-simple-async-result.c new file mode 100644 index 0000000..76e6cf6 --- /dev/null +++ b/e-util/e-simple-async-result.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gio/gio.h> + +#include "e-simple-async-result.h" + +struct _ESimpleAsyncResultPrivate { + GObject *source_object; + GAsyncReadyCallback callback; + gpointer callback_user_data; + gpointer source_tag; + + gpointer user_data; + GDestroyNotify destroy_user_data; + + gpointer op_pointer; +}; + +static void e_simple_async_result_iface_init (GAsyncResultIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ESimpleAsyncResult, e_simple_async_result, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, e_simple_async_result_iface_init)) + +static gpointer +e_simple_async_result_iface_get_user_data (GAsyncResult *result) +{ + ESimpleAsyncResult *eresult; + + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + eresult = E_SIMPLE_ASYNC_RESULT (result); + + return eresult->priv->callback_user_data; +} + +static GObject * +e_simple_async_result_iface_get_source_object (GAsyncResult *result) +{ + ESimpleAsyncResult *eresult; + + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + eresult = E_SIMPLE_ASYNC_RESULT (result); + + return eresult->priv->source_object; +} + +static gboolean +e_simple_async_result_iface_is_tagged (GAsyncResult *result, + gpointer source_tag) +{ + ESimpleAsyncResult *eresult; + + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + eresult = E_SIMPLE_ASYNC_RESULT (result); + + return eresult && eresult->priv->source_tag == source_tag; +} + +static void +e_simple_async_result_iface_init (GAsyncResultIface *iface) +{ + iface->get_user_data = e_simple_async_result_iface_get_user_data; + iface->get_source_object = e_simple_async_result_iface_get_source_object; + iface->is_tagged = e_simple_async_result_iface_is_tagged; +} + +static void +e_simple_async_result_finalize (GObject *object) +{ + ESimpleAsyncResult *result = E_SIMPLE_ASYNC_RESULT (object); + + if (result->priv->user_data && result->priv->destroy_user_data) + result->priv->destroy_user_data (result->priv->user_data); + + result->priv->destroy_user_data = NULL; + result->priv->user_data = NULL; + + g_clear_object (&result->priv->source_object); + + /* Chain up to parent's method */ + G_OBJECT_CLASS (e_simple_async_result_parent_class)->finalize (object); +} + +static void +e_simple_async_result_class_init (ESimpleAsyncResultClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ESimpleAsyncResultPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = e_simple_async_result_finalize; +} + +static void +e_simple_async_result_init (ESimpleAsyncResult *result) +{ + result->priv = G_TYPE_INSTANCE_GET_PRIVATE (result, E_TYPE_SIMPLE_ASYNC_RESULT, ESimpleAsyncResultPrivate); +} + +ESimpleAsyncResult * +e_simple_async_result_new (GObject *source_object, + GAsyncReadyCallback callback, + gpointer user_data, + gpointer source_tag) +{ + ESimpleAsyncResult *result; + + g_return_val_if_fail (callback != NULL, NULL); + if (source_object) + g_return_val_if_fail (G_IS_OBJECT (source_object), NULL); + + result = g_object_new (E_TYPE_SIMPLE_ASYNC_RESULT, NULL); + + result->priv->source_object = source_object ? g_object_ref (source_object) : NULL; + result->priv->callback = callback; + result->priv->callback_user_data = user_data; + result->priv->source_tag = source_tag; + + return result; +} + +void +e_simple_async_result_set_user_data (ESimpleAsyncResult *result, + gpointer user_data, + GDestroyNotify destroy_user_data) +{ + ESimpleAsyncResult *eresult; + + g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result)); + + eresult = E_SIMPLE_ASYNC_RESULT (result); + + if (eresult->priv->user_data == user_data) + return; + + if (eresult->priv->user_data && eresult->priv->destroy_user_data) + eresult->priv->destroy_user_data (eresult->priv->user_data); + + eresult->priv->user_data = user_data; + eresult->priv->destroy_user_data = destroy_user_data; +} + +gpointer +e_simple_async_result_get_user_data (ESimpleAsyncResult *result) +{ + ESimpleAsyncResult *eresult; + + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + eresult = E_SIMPLE_ASYNC_RESULT (result); + + return eresult->priv->user_data; +} + +gpointer +e_simple_async_result_steal_user_data (ESimpleAsyncResult *result) +{ + ESimpleAsyncResult *eresult; + gpointer user_data; + + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + eresult = E_SIMPLE_ASYNC_RESULT (result); + + user_data = eresult->priv->user_data; + + eresult->priv->user_data = NULL; + eresult->priv->destroy_user_data = NULL; + + return user_data; +} + +void +e_simple_async_result_set_op_pointer (ESimpleAsyncResult *result, + gpointer ptr) +{ + g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result)); + + result->priv->op_pointer = ptr; +} + +gpointer +e_simple_async_result_get_op_pointer (ESimpleAsyncResult *result) +{ + g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + return result->priv->op_pointer; +} + +void +e_simple_async_result_complete (ESimpleAsyncResult *result) +{ + g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result)); + + g_object_ref (result); + + result->priv->callback (result->priv->source_object, G_ASYNC_RESULT (result), result->priv->callback_user_data); + + g_object_unref (result); +} diff --git a/e-util/e-simple-async-result.h b/e-util/e-simple-async-result.h new file mode 100644 index 0000000..3800de1 --- /dev/null +++ b/e-util/e-simple-async-result.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION) +#error "Only <e-util/e-util.h> should be included directly." +#endif + +#ifndef E_SIMPLE_ASYNC_RESULT_H +#define E_SIMPLE_ASYNC_RESULT_H + +#include <gio/gio.h> + +/* Standard GObject macros */ +#define E_TYPE_SIMPLE_ASYNC_RESULT \ + (e_simple_async_result_get_type ()) +#define E_SIMPLE_ASYNC_RESULT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SIMPLE_ASYNC_RESULT, ESimpleAsyncResult)) +#define E_SIMPLE_ASYNC_RESULT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SIMPLE_ASYNC_RESULT, ESimpleAsyncResultClass)) +#define E_IS_SIMPLE_ASYNC_RESULT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SIMPLE_ASYNC_RESULT)) +#define E_IS_SIMPLE_ASYNC_RESULT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SIMPLE_ASYNC_RESULT)) +#define E_SIMPLE_ASYNC_RESULT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SIMPLE_ASYNC_RESULT, ESimpleAsyncResultClass)) + +G_BEGIN_DECLS + +typedef struct _ESimpleAsyncResult ESimpleAsyncResult; +typedef struct _ESimpleAsyncResultClass ESimpleAsyncResultClass; +typedef struct _ESimpleAsyncResultPrivate ESimpleAsyncResultPrivate; + +/** + * ESimpleAsyncResult: + * + * Contains only private data that should be read and manipulated using the + * functions below. + **/ +struct _ESimpleAsyncResult { + GObject parent; + + ESimpleAsyncResultPrivate *priv; +}; + +struct _ESimpleAsyncResultClass { + GObjectClass parent_class; +}; + +GType e_simple_async_result_get_type (void) G_GNUC_CONST; +ESimpleAsyncResult * + e_simple_async_result_new (GObject *source_object, + GAsyncReadyCallback callback, + gpointer user_data, + gpointer source_tag); +void e_simple_async_result_set_user_data + (ESimpleAsyncResult *result, + gpointer user_data, + GDestroyNotify destroy_user_data); +gpointer e_simple_async_result_get_user_data + (ESimpleAsyncResult *result); +gpointer e_simple_async_result_steal_user_data + (ESimpleAsyncResult *result); +void e_simple_async_result_set_op_pointer + (ESimpleAsyncResult *result, + gpointer ptr); +gpointer e_simple_async_result_get_op_pointer + (ESimpleAsyncResult *result); +void e_simple_async_result_complete (ESimpleAsyncResult *result); + +G_END_DECLS + +#endif /* E_SIMPLE_ASYNC_RESULT_H */ diff --git a/e-util/e-spell-checker.c b/e-util/e-spell-checker.c index 6cfccf6..dbdd895 100644 --- a/e-util/e-spell-checker.c +++ b/e-util/e-spell-checker.c @@ -24,7 +24,6 @@ #include "e-spell-dictionary.h" #include <libebackend/libebackend.h> -#include <webkit/webkitspellchecker.h> #include <pango/pango.h> #include <gtk/gtk.h> #include <string.h> @@ -45,27 +44,19 @@ enum { PROP_ACTIVE_LANGUAGES }; -/* Forward Declarations */ -static void e_spell_checker_init_webkit_checker - (WebKitSpellCheckerInterface *interface); - G_DEFINE_TYPE_EXTENDED ( ESpellChecker, e_spell_checker, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE ( - E_TYPE_EXTENSIBLE, NULL) - G_IMPLEMENT_INTERFACE ( - WEBKIT_TYPE_SPELL_CHECKER, - e_spell_checker_init_webkit_checker)) + E_TYPE_EXTENSIBLE, NULL)) /** * ESpellChecker: * * #ESpellChecker represents a spellchecker in Evolution. It can be used as a - * provider for dictionaries. It also implements #WebKitSpellCheckerInterface, - * so it can be set as a default spell-checker to WebKit editors + * provider for dictionaries. */ @@ -92,237 +83,6 @@ spell_checker_enchant_dicts_foreach_cb (gpointer key, } static void -wksc_check_spelling (WebKitSpellChecker *webkit_checker, - const gchar *word, - gint *misspelling_location, - gint *misspelling_length) -{ - ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker); - GHashTable *active_dictionaries; - PangoLanguage *language; - PangoLogAttr *attrs; - gint length, ii; - - active_dictionaries = checker->priv->active_dictionaries; - if (g_hash_table_size (active_dictionaries) == 0) - return; - - length = g_utf8_strlen (word, -1); - - language = pango_language_get_default (); - attrs = g_new (PangoLogAttr, length + 1); - - pango_get_log_attrs (word, -1, -1, language, attrs, length + 1); - - for (ii = 0; ii < length + 1; ii++) { - /* We go through each character until we find an is_word_start, - * then we get into an inner loop to find the is_word_end - * corresponding */ - if (attrs[ii].is_word_start) { - gboolean word_recognized; - gint start = ii; - gint end = ii; - gint word_length; - gchar *cstart; - gint bytes; - gchar *new_word; - - while (attrs[end].is_word_end < 1) - end++; - - word_length = end - start; - /* Set the iterator to be at the current word - * end, so we don't check characters twice. */ - ii = end; - - cstart = g_utf8_offset_to_pointer (word, start); - bytes = g_utf8_offset_to_pointer (word, end) - cstart; - new_word = g_new0 (gchar, bytes + 1); - - g_utf8_strncpy (new_word, cstart, word_length); - - word_recognized = e_spell_checker_check_word ( - checker, new_word, strlen (new_word)); - - if (word_recognized) { - if (misspelling_location != NULL) - *misspelling_location = -1; - if (misspelling_length != NULL) - *misspelling_length = 0; - } else { - if (misspelling_location != NULL) - *misspelling_location = start; - if (misspelling_length != NULL) - *misspelling_length = word_length; - } - - g_free (new_word); - } - } - - g_free (attrs); -} - -static gchar ** -wksc_get_guesses (WebKitSpellChecker *webkit_checker, - const gchar *word, - const gchar *context) -{ - ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker); - GHashTable *active_dictionaries; - GList *list, *link; - gchar ** guesses; - gint ii = 0; - - guesses = g_new0 (gchar *, MAX_SUGGESTIONS + 1); - - active_dictionaries = checker->priv->active_dictionaries; - list = g_hash_table_get_keys (active_dictionaries); - - for (link = list; link != NULL; link = g_list_next (link)) { - ESpellDictionary *dictionary; - GList *suggestions; - - dictionary = E_SPELL_DICTIONARY (link->data); - suggestions = e_spell_dictionary_get_suggestions ( - dictionary, word, -1); - - while (suggestions != NULL && ii < MAX_SUGGESTIONS) { - guesses[ii++] = suggestions->data; - suggestions->data = NULL; - - suggestions = g_list_delete_link ( - suggestions, suggestions); - } - - g_list_free_full (suggestions, (GDestroyNotify) g_free); - - if (ii >= MAX_SUGGESTIONS) - break; - } - - g_list_free (list); - - return guesses; -} - -static gchar * -wksc_get_autocorrect_suggestions (WebKitSpellChecker *webkit_checker, - const gchar *word) -{ - /* Not supported/needed */ - return NULL; -} - -static void -spell_checker_learn_word (WebKitSpellChecker *webkit_checker, - const gchar *word) -{ - /* Carefully, this will add the word to all active dictionaries! */ - - ESpellChecker *checker; - GHashTable *active_dictionaries; - GList *list, *link; - - checker = E_SPELL_CHECKER (webkit_checker); - active_dictionaries = checker->priv->active_dictionaries; - list = g_hash_table_get_keys (active_dictionaries); - - for (link = list; link != NULL; link = g_list_next (link)) { - ESpellDictionary *dictionary; - - dictionary = E_SPELL_DICTIONARY (link->data); - e_spell_dictionary_learn_word (dictionary, word, -1); - } - - g_list_free (list); -} - -static void -spell_checker_ignore_word (WebKitSpellChecker *webkit_checker, - const gchar *word) -{ - /* Carefully, this will add the word to all active dictionaries */ - - ESpellChecker *checker; - GHashTable *active_dictionaries; - GList *list, *link; - - checker = E_SPELL_CHECKER (webkit_checker); - active_dictionaries = checker->priv->active_dictionaries; - list = g_hash_table_get_keys (active_dictionaries); - - for (link = list; link != NULL; link = g_list_next (link)) { - ESpellDictionary *dictionary; - - dictionary = E_SPELL_DICTIONARY (link->data); - e_spell_dictionary_ignore_word (dictionary, word, -1); - } - - g_list_free (list); -} - -static void -wksc_update_languages (WebKitSpellChecker *webkit_checker, - const gchar *languages) -{ - ESpellChecker *checker; - GHashTable *active_dictionaries; - GQueue queue = G_QUEUE_INIT; - - checker = E_SPELL_CHECKER (webkit_checker); - active_dictionaries = checker->priv->active_dictionaries; - - if (languages != NULL) { - gchar **langs; - gint ii; - - langs = g_strsplit (languages, ",", -1); - for (ii = 0; langs[ii] != NULL; ii++) { - ESpellDictionary *dictionary; - - dictionary = e_spell_checker_ref_dictionary ( - checker, langs[ii]); - if (dictionary != NULL) - g_queue_push_tail (&queue, dictionary); - } - g_strfreev (langs); - } else { - ESpellDictionary *dictionary; - PangoLanguage *pango_language; - const gchar *language; - - pango_language = gtk_get_default_language (); - language = pango_language_to_string (pango_language); - dictionary = e_spell_checker_ref_dictionary (checker, language); - - if (dictionary == NULL) { - GList *list; - - list = e_spell_checker_list_available_dicts (checker); - if (list != NULL) { - dictionary = g_object_ref (list->data); - g_list_free (list); - } - } - - if (dictionary != NULL) - g_queue_push_tail (&queue, dictionary); - } - - g_hash_table_remove_all (active_dictionaries); - - while (!g_queue_is_empty (&queue)) { - ESpellDictionary *dictionary; - - dictionary = g_queue_pop_head (&queue); - g_hash_table_add (active_dictionaries, dictionary); - } - - g_object_notify (G_OBJECT (checker), "active-languages"); -} - -static void spell_checker_get_property (GObject *object, guint property_id, GValue *value, @@ -403,18 +163,6 @@ e_spell_checker_class_init (ESpellCheckerClass *class) } static void -e_spell_checker_init_webkit_checker (WebKitSpellCheckerInterface *interface) -{ - interface->check_spelling_of_string = wksc_check_spelling; - interface->get_autocorrect_suggestions_for_misspelled_word = - wksc_get_autocorrect_suggestions; - interface->get_guesses_for_word = wksc_get_guesses; - interface->ignore_word = spell_checker_ignore_word; - interface->learn_word = spell_checker_learn_word; - interface->update_spell_checking_languages = wksc_update_languages; -} - -static void e_spell_checker_init (ESpellChecker *checker) { GHashTable *active_dictionaries; @@ -573,6 +321,25 @@ e_spell_checker_list_available_dicts (ESpellChecker *checker) } /** + * e_spell_checker_count_available_dicts: + * @checker: An #ESpellChecker + * + * Returns: Count of available dictionaries. + **/ +guint +e_spell_checker_count_available_dicts (ESpellChecker *checker) +{ + g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), 0); + + if (g_hash_table_size (checker->priv->dictionaries_cache) == 0) { + e_spell_checker_init_global_memory (); + g_hash_table_foreach (global_language_tags, copy_enchant_dicts, checker); + } + + return g_hash_table_size (checker->priv->dictionaries_cache); +} + +/** * e_spell_checker_ref_dictionary: * @checker: an #ESpellChecker * @language_code: (allow-none): language code of a dictionary, or %NULL @@ -703,6 +470,42 @@ e_spell_checker_set_language_active (ESpellChecker *checker, } /** + * e_spell_checker_set_active_languages: + * @checker: An #ESpellChecker + * @languages: A list of languages to have activated + * + * Activates only the languages from @languages, all others will + * be deactivated after this function is finished. + **/ +void +e_spell_checker_set_active_languages (ESpellChecker *checker, + const gchar * const *languages) +{ + gint ii; + + g_return_if_fail (E_IS_SPELL_CHECKER (checker)); + + g_object_freeze_notify (G_OBJECT (checker)); + + for (ii = 0; languages && languages[ii]; ii++) { + e_spell_checker_set_language_active (checker, languages[ii], TRUE); + } + + if (ii == g_hash_table_size (checker->priv->active_dictionaries)) { + g_object_thaw_notify (G_OBJECT (checker)); + return; + } + + g_hash_table_remove_all (checker->priv->active_dictionaries); + for (ii = 0; languages && languages[ii]; ii++) { + e_spell_checker_set_language_active (checker, languages[ii], TRUE); + } + + g_object_notify (G_OBJECT (checker), "active-languages"); + g_object_thaw_notify (G_OBJECT (checker)); +} + +/** * e_spell_checker_list_active_languages: * @checker: an #ESpellChecker * @n_languages: return location for the number of active languages, or %NULL @@ -816,12 +619,24 @@ void e_spell_checker_ignore_word (ESpellChecker *checker, const gchar *word) { - WebKitSpellCheckerInterface *interface; + /* Carefully, this will add the word to all active dictionaries */ + + GHashTable *active_dictionaries; + GList *list, *link; g_return_if_fail (E_IS_SPELL_CHECKER (checker)); - interface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker); - interface->ignore_word (WEBKIT_SPELL_CHECKER (checker), word); + active_dictionaries = checker->priv->active_dictionaries; + list = g_hash_table_get_keys (active_dictionaries); + + for (link = list; link != NULL; link = g_list_next (link)) { + ESpellDictionary *dictionary; + + dictionary = E_SPELL_DICTIONARY (link->data); + e_spell_dictionary_ignore_word (dictionary, word, -1); + } + + g_list_free (list); } /** @@ -836,10 +651,74 @@ void e_spell_checker_learn_word (ESpellChecker *checker, const gchar *word) { - WebKitSpellCheckerInterface *interface; + /* Carefully, this will add the word to all active dictionaries! */ + + GHashTable *active_dictionaries; + GList *list, *link; g_return_if_fail (E_IS_SPELL_CHECKER (checker)); - interface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker); - interface->learn_word (WEBKIT_SPELL_CHECKER (checker), word); + active_dictionaries = checker->priv->active_dictionaries; + list = g_hash_table_get_keys (active_dictionaries); + + for (link = list; link != NULL; link = g_list_next (link)) { + ESpellDictionary *dictionary; + + dictionary = E_SPELL_DICTIONARY (link->data); + e_spell_dictionary_learn_word (dictionary, word, -1); + } + + g_list_free (list); +} + +/** + * e_spell_checker_get_guesses_for_word: + * @checker: an #ESpellChecker + * @word: word to get guesses for + * + * Returns: a NULL-terminated array of guesses for the @word. Free the returned + * pointer with g_strfreev() when done with it. + **/ +gchar ** +e_spell_checker_get_guesses_for_word (ESpellChecker *checker, + const gchar *word) +{ + GHashTable *active_dictionaries; + GList *list, *link; + gchar **guesses; + gint ii = 0; + + g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL); + g_return_val_if_fail (word != NULL, NULL); + + guesses = g_new0 (gchar *, MAX_SUGGESTIONS + 1); + + active_dictionaries = checker->priv->active_dictionaries; + list = g_hash_table_get_keys (active_dictionaries); + + for (link = list; link != NULL; link = g_list_next (link)) { + ESpellDictionary *dictionary; + GList *suggestions; + + dictionary = E_SPELL_DICTIONARY (link->data); + suggestions = e_spell_dictionary_get_suggestions ( + dictionary, word, -1); + + while (suggestions != NULL && ii < MAX_SUGGESTIONS) { + guesses[ii++] = suggestions->data; + suggestions->data = NULL; + + suggestions = g_list_delete_link ( + suggestions, suggestions); + } + + g_list_free_full (suggestions, (GDestroyNotify) g_free); + + if (ii >= MAX_SUGGESTIONS) + break; + } + + g_list_free (list); + + return guesses; } diff --git a/e-util/e-spell-checker.h b/e-util/e-spell-checker.h index d986e84..690401b 100644 --- a/e-util/e-spell-checker.h +++ b/e-util/e-spell-checker.h @@ -66,6 +66,8 @@ void e_spell_checker_free_global_memory ESpellChecker * e_spell_checker_new (void); GList * e_spell_checker_list_available_dicts (ESpellChecker *checker); +guint e_spell_checker_count_available_dicts + (ESpellChecker *checker); ESpellDictionary * e_spell_checker_ref_dictionary (ESpellChecker *checker, const gchar *language_code); @@ -79,6 +81,9 @@ void e_spell_checker_set_language_active (ESpellChecker *checker, const gchar *language_code, gboolean active); +void e_spell_checker_set_active_languages + (ESpellChecker *checker, + const gchar * const *languages); gchar ** e_spell_checker_list_active_languages (ESpellChecker *checker, guint *n_languages); @@ -91,6 +96,9 @@ void e_spell_checker_learn_word (ESpellChecker *checker, const gchar *word); void e_spell_checker_ignore_word (ESpellChecker *checker, const gchar *word); +gchar ** e_spell_checker_get_guesses_for_word + (ESpellChecker *checker, + const gchar *word); G_END_DECLS diff --git a/e-util/e-stock-request.c b/e-util/e-stock-request.c index 5d70fa3..c550a2c 100644 --- a/e-util/e-stock-request.c +++ b/e-util/e-stock-request.c @@ -15,56 +15,87 @@ * */ -#define LIBSOUP_USE_UNSTABLE_REQUEST_API - -#include "e-stock-request.h" +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif #include <stdlib.h> +#include <string.h> + #include <gtk/gtk.h> #include <libsoup/soup.h> -#include <string.h> +#include <libedataserver/libedataserver.h> -#define E_STOCK_REQUEST_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_STOCK_REQUEST, EStockRequestPrivate)) +#include "e-misc-utils.h" +#include "e-stock-request.h" struct _EStockRequestPrivate { - gchar *content_type; - gint content_length; + gint dummy; }; -static const gchar *data_schemes[] = { "gtk-stock", NULL }; +static void e_stock_request_content_request_init (EContentRequestInterface *iface); -G_DEFINE_TYPE (EStockRequest, e_stock_request, SOUP_TYPE_REQUEST) +G_DEFINE_TYPE_WITH_CODE (EStockRequest, e_stock_request, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_stock_request_content_request_init)) static gboolean -handle_stock_request_idle_cb (gpointer user_data) +e_stock_request_can_process_uri (EContentRequest *request, + const gchar *uri) { - EStockRequestPrivate *priv; - GSimpleAsyncResult *simple; - GObject *object; - SoupURI *uri; + g_return_val_if_fail (E_IS_STOCK_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + + return g_ascii_strncasecmp (uri, "gtk-stock:", 10) == 0; +} + +typedef struct _StockIdleData +{ + EContentRequest *request; + const gchar *uri; + GObject *requester; + GInputStream **out_stream; + gint64 *out_stream_length; + gchar **out_mime_type; + GCancellable *cancellable; + GError **error; + + gboolean success; + EFlag *flag; +} StockIdleData; + +static gboolean +process_stock_request_idle_cb (gpointer user_data) +{ + StockIdleData *sid = user_data; + SoupURI *suri; GHashTable *query = NULL; GtkStyleContext *context; GtkWidgetPath *path; GtkIconSet *icon_set; gssize size = GTK_ICON_SIZE_BUTTON; gchar *a_size; - gchar *buffer = NULL; + gchar *buffer = NULL, *mime_type = NULL; gsize buff_len = 0; GError *local_error = NULL; - simple = G_SIMPLE_ASYNC_RESULT (user_data); + g_return_val_if_fail (sid != NULL, FALSE); + g_return_val_if_fail (E_IS_STOCK_REQUEST (sid->request), FALSE); + g_return_val_if_fail (sid->uri != NULL, FALSE); + g_return_val_if_fail (sid->flag != NULL, FALSE); - /* This returns a new reference. */ - object = g_async_result_get_source_object (G_ASYNC_RESULT (simple)); + if (g_cancellable_set_error_if_cancelled (sid->cancellable, sid->error)) { + sid->success = FALSE; + e_flag_set (sid->flag); - priv = E_STOCK_REQUEST_GET_PRIVATE (object); + return FALSE; + } + + suri = soup_uri_new (sid->uri); + g_return_val_if_fail (suri != NULL, FALSE); - uri = soup_request_get_uri (SOUP_REQUEST (object)); - if (uri->query != NULL) - query = soup_form_decode (uri->query); + if (suri->query != NULL) + query = soup_form_decode (suri->query); if (query != NULL) { a_size = g_hash_table_lookup (query, "size"); @@ -81,7 +112,7 @@ handle_stock_request_idle_cb (gpointer user_data) gtk_style_context_set_path (context, path); gtk_widget_path_free (path); - icon_set = gtk_style_context_lookup_icon_set (context, uri->host); + icon_set = gtk_style_context_lookup_icon_set (context, suri->host); if (icon_set != NULL) { GdkPixbuf *pixbuf; @@ -97,11 +128,19 @@ handle_stock_request_idle_cb (gpointer user_data) GtkIconTheme *icon_theme; GtkIconInfo *icon_info; const gchar *filename; + gint icon_width, icon_height; + + if (!gtk_icon_size_lookup (size, &icon_width, &icon_height)) { + icon_width = size; + icon_height = size; + } + + size = MAX (icon_width, icon_height); icon_theme = gtk_icon_theme_get_default (); icon_info = gtk_icon_theme_lookup_icon ( - icon_theme, uri->host, size, + icon_theme, suri->host, size, GTK_ICON_LOOKUP_USE_BUILTIN); /* Some icons can be missing in the theme */ @@ -113,9 +152,7 @@ handle_stock_request_idle_cb (gpointer user_data) buffer = NULL; buff_len = 0; } - priv->content_type = - g_content_type_guess (filename, NULL, 0, NULL); - + mime_type = g_content_type_guess (filename, NULL, 0, NULL); } else { GdkPixbuf *pixbuf; @@ -129,156 +166,141 @@ handle_stock_request_idle_cb (gpointer user_data) } gtk_icon_info_free (icon_info); - } - } - - /* Sanity check */ - g_warn_if_fail ( - ((buffer != NULL) && (local_error == NULL)) || - ((buffer == NULL) && (local_error != NULL))); - - if (priv->content_type == NULL) - priv->content_type = g_strdup ("image/png"); - priv->content_length = buff_len; - - if (buffer != NULL) { - GInputStream *stream; - - stream = g_memory_input_stream_new_from_data ( - buffer, buff_len, (GDestroyNotify) g_free); - g_simple_async_result_set_op_res_gpointer ( - simple, g_object_ref (stream), - (GDestroyNotify) g_object_unref); - g_object_unref (stream); - } - - if (local_error != NULL) - g_simple_async_result_take_error (simple, local_error); - - g_simple_async_result_complete_in_idle (simple); - - g_object_unref (context); - g_object_unref (object); + } else if (g_strcmp0 (suri->host, "x-evolution-arrow-down") == 0) { + GdkPixbuf *pixbuf; + GdkRGBA rgba; + guchar *data; + gint stride; + cairo_surface_t *surface; + cairo_t *cr; - return FALSE; -} + stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, size); + buff_len = stride * size; + data = g_malloc0 (buff_len); + surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, size, size, stride); -static void -stock_request_finalize (GObject *object) -{ - EStockRequestPrivate *priv; + cr = cairo_create (surface); - priv = E_STOCK_REQUEST_GET_PRIVATE (object); + if (gtk_style_context_lookup_color (context, "color", &rgba)) + gdk_cairo_set_source_rgba (cr, &rgba); + else + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); - g_free (priv->content_type); + gtk_render_background (context, cr, 0, 0, size, size); + gtk_render_arrow (context, cr, G_PI, 0, 0, size); - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_stock_request_parent_class)->finalize (object); -} + cairo_destroy (cr); -static gboolean -stock_request_check_uri (SoupRequest *request, - SoupURI *uri, - GError **error) -{ - return (strcmp (uri->scheme, "gtk-stock") == 0); -} + cairo_surface_flush (surface); -static void -stock_request_send_async (SoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; + pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE, 8, size, size, stride, NULL, NULL); + gdk_pixbuf_save_to_buffer ( + pixbuf, &buffer, &buff_len, + "png", &local_error, NULL); + g_object_unref (pixbuf); - simple = g_simple_async_result_new ( - G_OBJECT (request), callback, user_data, - stock_request_send_async); + cairo_surface_destroy (surface); + g_free (data); + } + } - g_simple_async_result_set_check_cancellable (simple, cancellable); + /* Sanity check */ + g_warn_if_fail ( + ((buffer != NULL) && (local_error == NULL)) || + ((buffer == NULL) && (local_error != NULL))); - /* Need to run this operation in an idle callback rather - * than a worker thread, since we're making all kinds of - * GdkPixbuf/GTK+ calls. */ - g_idle_add_full ( - G_PRIORITY_HIGH_IDLE, - handle_stock_request_idle_cb, - g_object_ref (simple), - (GDestroyNotify) g_object_unref); + if (!mime_type) + mime_type = g_strdup ("image/png"); - g_object_unref (simple); -} + if (buffer != NULL) { + *sid->out_stream = g_memory_input_stream_new_from_data (buffer, buff_len, g_free);; + *sid->out_stream_length = buff_len; + *sid->out_mime_type = mime_type; -static GInputStream * -stock_request_send_finish (SoupRequest *request, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - GInputStream *stream; + sid->success = TRUE; + } else { + g_free (mime_type); - simple = G_SIMPLE_ASYNC_RESULT (result); - stream = g_simple_async_result_get_op_res_gpointer (simple); + if (local_error) + g_propagate_error (sid->error, local_error); - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; + sid->success = FALSE; + } - /* Reset the stream before passing it back to WebKit. */ - if (G_IS_SEEKABLE (stream)) - g_seekable_seek ( - G_SEEKABLE (stream), 0, - G_SEEK_SET, NULL, NULL); + soup_uri_free (suri); + g_object_unref (context); - if (stream != NULL) - return g_object_ref (stream); + e_flag_set (sid->flag); - return g_memory_input_stream_new (); + return FALSE; } -static goffset -stock_request_get_content_length (SoupRequest *request) +static gboolean +e_stock_request_process_sync (EContentRequest *request, + const gchar *uri, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error) { - EStockRequestPrivate *priv; + StockIdleData sid; + + g_return_val_if_fail (E_IS_STOCK_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + + sid.request = request; + sid.uri = uri; + sid.requester = requester; + sid.out_stream = out_stream; + sid.out_stream_length = out_stream_length; + sid.out_mime_type = out_mime_type; + sid.cancellable = cancellable; + sid.error = error; + sid.flag = e_flag_new (); + sid.success = FALSE; + + if (e_util_is_main_thread (NULL)) { + process_stock_request_idle_cb (&sid); + } else { + /* Need to run this operation in an idle callback rather + * than a worker thread, since we're making all kinds of + * GdkPixbuf/GTK+ calls. */ + g_idle_add_full ( + G_PRIORITY_HIGH_IDLE, + process_stock_request_idle_cb, + &sid, NULL); + + e_flag_wait (sid.flag); + } - priv = E_STOCK_REQUEST_GET_PRIVATE (request); + e_flag_free (sid.flag); - return priv->content_length; + return sid.success; } -static const gchar * -stock_request_get_content_type (SoupRequest *request) +static void +e_stock_request_content_request_init (EContentRequestInterface *iface) { - EStockRequestPrivate *priv; - - priv = E_STOCK_REQUEST_GET_PRIVATE (request); - - return priv->content_type; + iface->can_process_uri = e_stock_request_can_process_uri; + iface->process_sync = e_stock_request_process_sync; } static void e_stock_request_class_init (EStockRequestClass *class) { - GObjectClass *object_class; - SoupRequestClass *request_class; - g_type_class_add_private (class, sizeof (EStockRequestPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = stock_request_finalize; - - request_class = SOUP_REQUEST_CLASS (class); - request_class->schemes = data_schemes; - request_class->check_uri = stock_request_check_uri; - request_class->send_async = stock_request_send_async; - request_class->send_finish = stock_request_send_finish; - request_class->get_content_length = stock_request_get_content_length; - request_class->get_content_type = stock_request_get_content_type; } static void e_stock_request_init (EStockRequest *request) { - request->priv = E_STOCK_REQUEST_GET_PRIVATE (request); + request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_STOCK_REQUEST, EStockRequestPrivate); } +EContentRequest * +e_stock_request_new (void) +{ + return g_object_new (E_TYPE_STOCK_REQUEST, NULL); +} diff --git a/e-util/e-stock-request.h b/e-util/e-stock-request.h index be6222b..09800d0 100644 --- a/e-util/e-stock-request.h +++ b/e-util/e-stock-request.h @@ -22,10 +22,7 @@ #ifndef E_STOCK_REQUEST_H #define E_STOCK_REQUEST_H -#define LIBSOUP_USE_UNSTABLE_REQUEST_API - -#include <libsoup/soup.h> -#include <libsoup/soup-request.h> +#include <e-util/e-content-request.h> /* Standard GObject macros */ #define E_TYPE_STOCK_REQUEST \ @@ -53,15 +50,17 @@ typedef struct _EStockRequestClass EStockRequestClass; typedef struct _EStockRequestPrivate EStockRequestPrivate; struct _EStockRequest { - SoupRequest parent; + GObject parent; EStockRequestPrivate *priv; }; struct _EStockRequestClass { - SoupRequestClass parent; + GObjectClass parent; }; GType e_stock_request_get_type (void) G_GNUC_CONST; +EContentRequest * + e_stock_request_new (void); G_END_DECLS diff --git a/e-util/e-util-enums.h b/e-util/e-util-enums.h index d24aebd..884c041 100644 --- a/e-util/e-util-enums.h +++ b/e-util/e-util-enums.h @@ -124,226 +124,415 @@ typedef enum { E_DURATION_DAYS } EDurationType; +/** + * EImageLoadingPolicy: + * @E_IMAGE_LOADING_POLICY_NEVER: + * Never load images from a remote server. + * @E_IMAGE_LOADING_POLICY_SOMETIMES: + * Only load images from a remote server if the sender is a known contact. + * @E_IMAGE_LOADING_POLICY_ALWAYS: + * Always load images from a remote server. + * + * Policy for loading remote image URLs in email. Allowing images to be + * loaded from a remote server may have privacy implications. + **/ +typedef enum { + E_IMAGE_LOADING_POLICY_NEVER, + E_IMAGE_LOADING_POLICY_SOMETIMES, + E_IMAGE_LOADING_POLICY_ALWAYS +} EImageLoadingPolicy; + +/** + * EContentEditorInsertContentFlags: + * @E_CONTENT_EDITOR_INSERT_NONE: + * @E_CONTENT_EDITOR_INSERT_CONVERT: + * @E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT: + * @E_CONTENT_EDITOR_INSERT_REPLACE_ALL: + * @E_CONTENT_EDITOR_INSERT_TEXT_HTML: + * @E_CONTENT_EDITOR_INSERT_TEXT_PLAIN: + * + * Since: 3.22 + **/ +typedef enum { + E_CONTENT_EDITOR_INSERT_NONE = 0, + E_CONTENT_EDITOR_INSERT_CONVERT = 1 << 0, + E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT = 1 << 1, + E_CONTENT_EDITOR_INSERT_REPLACE_ALL = 1 << 2, + E_CONTENT_EDITOR_INSERT_TEXT_HTML = 1 << 3, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN = 1 << 4, +} EContentEditorInsertContentFlags; + +/** + * EContentEditorGetContentFlags: + * @E_CONTENT_EDITOR_GET_BODY: + * @E_CONTENT_EDITOR_GET_INLINE_IMAGES: + * @E_CONTENT_EDITOR_GET_PROCESSED: raw or processed + * @E_CONTENT_EDITOR_GET_TEXT_HTML: + * @E_CONTENT_EDITOR_GET_TEXT_PLAIN: + * @E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE: + * + * Since: 3.22 + **/ +typedef enum { + E_CONTENT_EDITOR_GET_BODY = 1 << 0, + E_CONTENT_EDITOR_GET_INLINE_IMAGES = 1 << 1, + E_CONTENT_EDITOR_GET_PROCESSED = 1 << 2, /* raw or processed */ + E_CONTENT_EDITOR_GET_TEXT_HTML = 1 << 3, + E_CONTENT_EDITOR_GET_TEXT_PLAIN = 1 << 4, + E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE = 1 << 5 +} EContentEditorGetContentFlags; + +/** + * EContentEditorNodeFlags: + * @E_CONTENT_EDITOR_NODE_UNKNOWN: None from the below, aka when cannot determine. + * @E_CONTENT_EDITOR_NODE_IS_ANCHOR: + * @E_CONTENT_EDITOR_NODE_IS_H_RULE: + * @E_CONTENT_EDITOR_NODE_IS_IMAGE: + * @E_CONTENT_EDITOR_NODE_IS_TABLE: + * @E_CONTENT_EDITOR_NODE_IS_TABLE_CELL: + * @E_CONTENT_EDITOR_NODE_IS_TEXT: + * @E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED: + * + * Since: 3.22 + **/ +typedef enum { + E_CONTENT_EDITOR_NODE_UNKNOWN = 0, + E_CONTENT_EDITOR_NODE_IS_ANCHOR = 1 << 0, + E_CONTENT_EDITOR_NODE_IS_H_RULE = 1 << 1, + E_CONTENT_EDITOR_NODE_IS_IMAGE = 1 << 2, + E_CONTENT_EDITOR_NODE_IS_TABLE = 1 << 3, + E_CONTENT_EDITOR_NODE_IS_TABLE_CELL = 1 << 4, + E_CONTENT_EDITOR_NODE_IS_TEXT = 1 << 5, + E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED = 1 << 6 +} EContentEditorNodeFlags; + +/** + * EContentEditorStyleFlags: + * @E_CONTENT_EDITOR_STYLE_NONE: None from the below. + * @E_CONTENT_EDITOR_STYLE_IS_BOLD: + * @E_CONTENT_EDITOR_STYLE_IS_ITALIC: + * @E_CONTENT_EDITOR_STYLE_IS_UNDERLINE: + * @E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH: + * @E_CONTENT_EDITOR_STYLE_IS_MONOSPACE: + * @E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT: + * @E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT: + * + * Since: 3.22 + **/ +typedef enum { + E_CONTENT_EDITOR_STYLE_NONE = 0, + E_CONTENT_EDITOR_STYLE_IS_BOLD = 1 << 0, + E_CONTENT_EDITOR_STYLE_IS_ITALIC = 1 << 1, + E_CONTENT_EDITOR_STYLE_IS_UNDERLINE = 1 << 2, + E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH = 1 << 3, + E_CONTENT_EDITOR_STYLE_IS_MONOSPACE = 1 << 4, + E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT = 1 << 5, + E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT = 1 << 6 +} EContentEditorStyleFlags; + +/** + * EContentEditorBlockFormat: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_NONE: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_PRE: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_H1: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_H2: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_H3: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_H4: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_H5: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_H6: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN: + * @E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA: + * + * Since: 3.22 + **/ typedef enum { - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE = 0, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN, - E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA -} EHTMLEditorSelectionBlockFormat; + E_CONTENT_EDITOR_BLOCK_FORMAT_NONE = 0, + E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH, + E_CONTENT_EDITOR_BLOCK_FORMAT_PRE, + E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS, + E_CONTENT_EDITOR_BLOCK_FORMAT_H1, + E_CONTENT_EDITOR_BLOCK_FORMAT_H2, + E_CONTENT_EDITOR_BLOCK_FORMAT_H3, + E_CONTENT_EDITOR_BLOCK_FORMAT_H4, + E_CONTENT_EDITOR_BLOCK_FORMAT_H5, + E_CONTENT_EDITOR_BLOCK_FORMAT_H6, + E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST, + E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST, + E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN, + E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA +} EContentEditorBlockFormat; -/* The values match the actual size in <font size="n"> */ +/** + * EContentEditorFontSize: + * @E_CONTENT_EDITOR_FONT_SIZE_TINY: + * @E_CONTENT_EDITOR_FONT_SIZE_SMALL: + * @E_CONTENT_EDITOR_FONT_SIZE_NORMAL: + * @E_CONTENT_EDITOR_FONT_SIZE_BIG: + * @E_CONTENT_EDITOR_FONT_SIZE_BIGGER: + * @E_CONTENT_EDITOR_FONT_SIZE_LARGE: + * @E_CONTENT_EDITOR_FONT_SIZE_VERY_LARGE: + * + * Note: The values match the actual size in <font size="n"> + * + * Since: 3.22 + **/ typedef enum { - E_HTML_EDITOR_SELECTION_FONT_SIZE_TINY = 1, - E_HTML_EDITOR_SELECTION_FONT_SIZE_SMALL = 2, - E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL = 3, - E_HTML_EDITOR_SELECTION_FONT_SIZE_BIG = 4, - E_HTML_EDITOR_SELECTION_FONT_SIZE_BIGGER = 5, - E_HTML_EDITOR_SELECTION_FONT_SIZE_LARGE = 6, - E_HTML_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE = 7 -} EHTMLEditorSelectionFontSize; + E_CONTENT_EDITOR_FONT_SIZE_TINY = 1, + E_CONTENT_EDITOR_FONT_SIZE_SMALL = 2, + E_CONTENT_EDITOR_FONT_SIZE_NORMAL = 3, + E_CONTENT_EDITOR_FONT_SIZE_BIG = 4, + E_CONTENT_EDITOR_FONT_SIZE_BIGGER = 5, + E_CONTENT_EDITOR_FONT_SIZE_LARGE = 6, + E_CONTENT_EDITOR_FONT_SIZE_VERY_LARGE = 7 +} EContentEditorFontSize; +/** + * EContentEditorAlignment: + * @E_CONTENT_EDITOR_ALIGNMENT_LEFT: + * @E_CONTENT_EDITOR_ALIGNMENT_CENTER: + * @E_CONTENT_EDITOR_ALIGNMENT_RIGHT: + * + * Since: 3.22 + **/ typedef enum { - E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT, - E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER, - E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT -} EHTMLEditorSelectionAlignment; + E_CONTENT_EDITOR_ALIGNMENT_LEFT = 0, + E_CONTENT_EDITOR_ALIGNMENT_CENTER, + E_CONTENT_EDITOR_ALIGNMENT_RIGHT +} EContentEditorAlignment; +/** + * EContentEditorGranularity: + * @E_CONTENT_EDITOR_GRANULARITY_CHARACTER: + * @E_CONTENT_EDITOR_GRANULARITY_WORD: + * + * Since: 3.22 + **/ typedef enum { - E_HTML_EDITOR_SELECTION_GRANULARITY_CHARACTER, - E_HTML_EDITOR_SELECTION_GRANULARITY_WORD -} EHTMLEditorSelectionGranularity; + E_CONTENT_EDITOR_GRANULARITY_CHARACTER = 0, + E_CONTENT_EDITOR_GRANULARITY_WORD +} EContentEditorGranularity; /** - * EHTMLEditorViewCommand: - * @E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR: + * EContentEditorCommand: + * @E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR: * Sets background color to given value. - * @E_HTML_EDITOR_VIEW_COMMAND_BOLD: + * @E_CONTENT_EDITOR_COMMAND_BOLD: * Toggles bold formatting of current selection. - * @E_HTML_EDITOR_VIEW_COMMAND_COPY: + * @E_CONTENT_EDITOR_COMMAND_COPY: * Copies current selection to clipboard. - * @E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK: + * @E_CONTENT_EDITOR_COMMAND_CREATE_LINK: * Converts current selection to a link that points to URL in value - * @E_HTML_EDITOR_VIEW_COMMAND_CUT: + * @E_CONTENT_EDITOR_COMMAND_CUT: * Cuts current selection to clipboard. - * @E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR: + * @E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR: * (XXX Explain me!) - * @E_HTML_EDITOR_VIEW_COMMAND_DELETE: + * @E_CONTENT_EDITOR_COMMAND_DELETE: * Deletes current selection. - * @E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING: + * @E_CONTENT_EDITOR_COMMAND_FIND_STRING: * Highlights given string. - * @E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME: + * @E_CONTENT_EDITOR_COMMAND_FONT_NAME: * Sets font name to given value. - * @E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE: + * @E_CONTENT_EDITOR_COMMAND_FONT_SIZE: * Sets font point size to given value (no units, just number) - * @E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA: + * @E_CONTENT_EDITOR_COMMAND_FONT_SIZE_DELTA: * Changes font size by given delta value (no units, just number) - * @E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR: + * @E_CONTENT_EDITOR_COMMAND_FORE_COLOR: * Sets font color to given value - * @E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK: + * @E_CONTENT_EDITOR_COMMAND_FORMAT_BLOCK: * Sets block type of current paragraph to given format. Allowed formats * are "BLOCKQUOTE", "H1", "H2", "H3", "H4", "H5", "H6", "P", "PRE" and * "ADDRESS". - * @E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE: + * @E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE: * (XXX Explain me!) - * @E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR: + * @E_CONTENT_EDITOR_COMMAND_HILITE_COLOR: * Sets color in which results of "FindString" command should be * highlighted to given value. - * @E_HTML_EDITOR_VIEW_COMMAND_INDENT: + * @E_CONTENT_EDITOR_COMMAND_INDENT: * Indents current paragraph by one level. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML: + * @E_CONTENT_EDITOR_COMMAND_INSERT_HTML: * Inserts give HTML code into document. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE: + * @E_CONTENT_EDITOR_COMMAND_INSERT_HORIZONTAL_RULE: * Inserts a horizontal rule (<HR>) on current line. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE: + * @E_CONTENT_EDITOR_COMMAND_INSERT_IMAGE: * Inserts an image with given source file. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK: + * @E_CONTENT_EDITOR_COMMAND_INSERT_LINE_BREAK: * Breaks line at current cursor position. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT: + * @E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT: * Breaks citation at current cursor position. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST: + * @E_CONTENT_EDITOR_COMMAND_INSERT_ORDERED_LIST: * Creates an ordered list environment at current cursor position. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH: + * @E_CONTENT_EDITOR_COMMAND_INSERT_PARAGRAPH: * Inserts a new paragraph at current cursor position. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT: + * @E_CONTENT_EDITOR_COMMAND_INSERT_TEXT: * Inserts given text at current cursor position. - * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST: + * @E_CONTENT_EDITOR_COMMAND_INSERT_UNORDERED_LIST: * Creates an undordered list environment at current cursor position. - * @E_HTML_EDITOR_VIEW_COMMAND_ITALIC: + * @E_CONTENT_EDITOR_COMMAND_ITALIC: * Toggles italic formatting of current selection. - * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER: + * @E_CONTENT_EDITOR_COMMAND_JUSTIFY_CENTER: * Aligns current paragraph to center. - * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL: + * @E_CONTENT_EDITOR_COMMAND_JUSTIFY_FULL: * Justifies current paragraph to block. - * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE: + * @E_CONTENT_EDITOR_COMMAND_JUSTIFY_NONE: * Removes any justification or alignment of current paragraph. - * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT: + * @E_CONTENT_EDITOR_COMMAND_JUSTIFY_RIGHT: * Aligns current paragraph to right. - * @E_HTML_EDITOR_VIEW_COMMAND_OUTDENT: + * @E_CONTENT_EDITOR_COMMAND_OUTDENT: * Outdents current paragraph by one level. - * @E_HTML_EDITOR_VIEW_COMMAND_PASTE: + * @E_CONTENT_EDITOR_COMMAND_PASTE: * Pastes clipboard content at current cursor position. - * @E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE: + * @E_CONTENT_EDITOR_COMMAND_PASTE_AND_MATCH_STYLE: * Pastes clipboard content and matches its style to style at current * cursor position. - * @E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT: + * @E_CONTENT_EDITOR_COMMAND_PASTE_AS_PLAIN_TEXT: * Pastes clipboard content at current cursor position removing any HTML * formatting. - * @E_HTML_EDITOR_VIEW_COMMAND_PRINT: + * @E_CONTENT_EDITOR_COMMAND_PRINT: * Print current document. - * @E_HTML_EDITOR_VIEW_COMMAND_REDO: + * @E_CONTENT_EDITOR_COMMAND_REDO: * Redoes last action. - * @E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT: + * @E_CONTENT_EDITOR_COMMAND_REMOVE_FORMAT: * Removes any formatting of current selection. - * @E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL: + * @E_CONTENT_EDITOR_COMMAND_SELECT_ALL: * Extends selects to the entire document. - * @E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH: + * @E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH: * Toggles strikethrough formatting. - * @E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS: + * @E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS: * Toggles whether style should be defined in CSS "style" attribute of * elements or whether to use deprecated <FONT> tags. Depends on * whether given value is "true" or "false". - * @E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT: + * @E_CONTENT_EDITOR_COMMAND_SUBSCRIPT: * Toggles subscript of current selection. - * @E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT: + * @E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT: * Toggles superscript of current selection. - * @E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE: + * @E_CONTENT_EDITOR_COMMAND_TRANSPOSE: * (XXX Explain me!) - * @E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE: + * @E_CONTENT_EDITOR_COMMAND_UNDERLINE: * Toggles underline formatting of current selection. - * @E_HTML_EDITOR_VIEW_COMMAND_UNDO: + * @E_CONTENT_EDITOR_COMMAND_UNDO: * Undoes last action. - * @E_HTML_EDITOR_VIEW_COMMAND_UNLINK: + * @E_CONTENT_EDITOR_COMMAND_UNLINK: * Removes active links (<A>) from current selection (if there's any). - * @E_HTML_EDITOR_VIEW_COMMAND_UNSELECT: + * @E_CONTENT_EDITOR_COMMAND_UNSELECT: * Cancels current selection. - * @E_HTML_EDITOR_VIEW_COMMAND_USE_CSS: + * @E_CONTENT_EDITOR_COMMAND_USE_CSS: * Whether to allow use of CSS or not depending on whether given value is * "true" or "false". * * Specifies the DOM command to execute in e_editor_widget_exec_command(). * Some commands require value to be passed in, which is always stated in the * documentation. - */ + * + * Since: 3.22 + **/ typedef enum { - E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR, - E_HTML_EDITOR_VIEW_COMMAND_BOLD, - E_HTML_EDITOR_VIEW_COMMAND_COPY, - E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK, - E_HTML_EDITOR_VIEW_COMMAND_CUT, - E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, - E_HTML_EDITOR_VIEW_COMMAND_DELETE, - E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING, - E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME, - E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE, - E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA, - E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR, - E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK, - E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE, - E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR, - E_HTML_EDITOR_VIEW_COMMAND_INDENT, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, - E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST, - E_HTML_EDITOR_VIEW_COMMAND_ITALIC, - E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER, - E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL, - E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT, - E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE, - E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT, - E_HTML_EDITOR_VIEW_COMMAND_OUTDENT, - E_HTML_EDITOR_VIEW_COMMAND_PASTE, - E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE, - E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT, - E_HTML_EDITOR_VIEW_COMMAND_PRINT, - E_HTML_EDITOR_VIEW_COMMAND_REDO, - E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT, - E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL, - E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, - E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, - E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT, - E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT, - E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE, - E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, - E_HTML_EDITOR_VIEW_COMMAND_UNDO, - E_HTML_EDITOR_VIEW_COMMAND_UNLINK, - E_HTML_EDITOR_VIEW_COMMAND_UNSELECT, - E_HTML_EDITOR_VIEW_COMMAND_USE_CSS -} EHTMLEditorViewCommand; + E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR, + E_CONTENT_EDITOR_COMMAND_BOLD, + E_CONTENT_EDITOR_COMMAND_COPY, + E_CONTENT_EDITOR_COMMAND_CREATE_LINK, + E_CONTENT_EDITOR_COMMAND_CUT, + E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, + E_CONTENT_EDITOR_COMMAND_DELETE, + E_CONTENT_EDITOR_COMMAND_FIND_STRING, + E_CONTENT_EDITOR_COMMAND_FONT_NAME, + E_CONTENT_EDITOR_COMMAND_FONT_SIZE, + E_CONTENT_EDITOR_COMMAND_FONT_SIZE_DELTA, + E_CONTENT_EDITOR_COMMAND_FORE_COLOR, + E_CONTENT_EDITOR_COMMAND_FORMAT_BLOCK, + E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE, + E_CONTENT_EDITOR_COMMAND_HILITE_COLOR, + E_CONTENT_EDITOR_COMMAND_INDENT, + E_CONTENT_EDITOR_COMMAND_INSERT_HTML, + E_CONTENT_EDITOR_COMMAND_INSERT_HORIZONTAL_RULE, + E_CONTENT_EDITOR_COMMAND_INSERT_IMAGE, + E_CONTENT_EDITOR_COMMAND_INSERT_LINE_BREAK, + E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, + E_CONTENT_EDITOR_COMMAND_INSERT_ORDERED_LIST, + E_CONTENT_EDITOR_COMMAND_INSERT_PARAGRAPH, + E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, + E_CONTENT_EDITOR_COMMAND_INSERT_UNORDERED_LIST, + E_CONTENT_EDITOR_COMMAND_ITALIC, + E_CONTENT_EDITOR_COMMAND_JUSTIFY_CENTER, + E_CONTENT_EDITOR_COMMAND_JUSTIFY_FULL, + E_CONTENT_EDITOR_COMMAND_JUSTIFY_LEFT, + E_CONTENT_EDITOR_COMMAND_JUSTIFY_NONE, + E_CONTENT_EDITOR_COMMAND_JUSTIFY_RIGHT, + E_CONTENT_EDITOR_COMMAND_OUTDENT, + E_CONTENT_EDITOR_COMMAND_PASTE, + E_CONTENT_EDITOR_COMMAND_PASTE_AND_MATCH_STYLE, + E_CONTENT_EDITOR_COMMAND_PASTE_AS_PLAIN_TEXT, + E_CONTENT_EDITOR_COMMAND_PRINT, + E_CONTENT_EDITOR_COMMAND_REDO, + E_CONTENT_EDITOR_COMMAND_REMOVE_FORMAT, + E_CONTENT_EDITOR_COMMAND_SELECT_ALL, + E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH, + E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS, + E_CONTENT_EDITOR_COMMAND_SUBSCRIPT, + E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT, + E_CONTENT_EDITOR_COMMAND_TRANSPOSE, + E_CONTENT_EDITOR_COMMAND_UNDERLINE, + E_CONTENT_EDITOR_COMMAND_UNDO, + E_CONTENT_EDITOR_COMMAND_UNLINK, + E_CONTENT_EDITOR_COMMAND_UNSELECT, + E_CONTENT_EDITOR_COMMAND_USE_CSS +} EContentEditorCommand; /** - * EImageLoadingPolicy: - * @E_IMAGE_LOADING_POLICY_NEVER: - * Never load images from a remote server. - * @E_IMAGE_LOADING_POLICY_SOMETIMES: - * Only load images from a remote server if the sender is a known contact. - * @E_IMAGE_LOADING_POLICY_ALWAYS: - * Always load images from a remote server. + * EContentEditorScope: + * @E_CONTENT_EDITOR_SCOPE_CELL: + * @E_CONTENT_EDITOR_SCOPE_ROW: + * @E_CONTENT_EDITOR_SCOPE_COLUMN: + * @E_CONTENT_EDITOR_SCOPE_TABLE: * - * Policy for loading remote image URLs in email. Allowing images to be - * loaded from a remote server may have privacy implications. + * Since: 3.22 **/ typedef enum { - E_IMAGE_LOADING_POLICY_NEVER, - E_IMAGE_LOADING_POLICY_SOMETIMES, - E_IMAGE_LOADING_POLICY_ALWAYS -} EImageLoadingPolicy; + E_CONTENT_EDITOR_SCOPE_CELL = 0, + E_CONTENT_EDITOR_SCOPE_ROW, + E_CONTENT_EDITOR_SCOPE_COLUMN, + E_CONTENT_EDITOR_SCOPE_TABLE +} EContentEditorScope; + +/** + * EContentEditorUnit: + * @E_CONTENT_EDITOR_UNIT_AUTO: + * @E_CONTENT_EDITOR_UNIT_PIXEL: + * @E_CONTENT_EDITOR_UNIT_PERCENTAGE: + * + * Since: 3.22 + **/ +typedef enum { + E_CONTENT_EDITOR_UNIT_AUTO = 0, + E_CONTENT_EDITOR_UNIT_PIXEL, + E_CONTENT_EDITOR_UNIT_PERCENTAGE +} EContentEditorUnit; + +/** + * EContentEditorCommand: + * @E_CONTENT_EDITOR_FIND_NEXT: Search for the next occurrence of the text. + * This is the default. It's mutually exclusive with @E_CONTENT_EDITOR_FIND_PREVIOUS. + * @E_CONTENT_EDITOR_FIND_PREVIOUS: Search for the previous occurrence of the text. + * It's mutually exclusive with @E_CONTENT_EDITOR_FIND_NEXT. + * @E_CONTENT_EDITOR_FIND_MODE_BACKWARDS: The search mode is backwards. If not set, + * then the mode is forward. + * @E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE: Search case insensitively. + * @E_CONTENT_EDITOR_FIND_WRAP_AROUND: Wrap around when searching. + * + * Flags to use to modify behaviour of the search for the text. + * + * Since: 3.22 + **/ +typedef enum { + E_CONTENT_EDITOR_FIND_NEXT = 1 << 0, + E_CONTENT_EDITOR_FIND_PREVIOUS = 1 << 1, + E_CONTENT_EDITOR_FIND_MODE_BACKWARDS = 1 << 2, + E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE = 1 << 3, + E_CONTENT_EDITOR_FIND_WRAP_AROUND = 1 << 4 +} EContentEditorFindFlags; G_END_DECLS diff --git a/e-util/e-util.h b/e-util/e-util.h index aa4bf14..8b3d163 100644 --- a/e-util/e-util.h +++ b/e-util/e-util.h @@ -32,7 +32,6 @@ #include <e-util/e-alert-sink.h> #include <e-util/e-alert.h> #include <e-util/e-attachment-bar.h> -#include <e-util/e-attachment-button.h> #include <e-util/e-attachment-dialog.h> #include <e-util/e-attachment-handler-image.h> #include <e-util/e-attachment-handler.h> @@ -86,6 +85,8 @@ #include <e-util/e-config.h> #include <e-util/e-conflict-search-selector.h> #include <e-util/e-contact-store.h> +#include <e-util/e-content-editor.h> +#include <e-util/e-content-request.h> #include <e-util/e-data-capture.h> #include <e-util/e-dateedit.h> #include <e-util/e-datetime-format.h> @@ -111,6 +112,7 @@ #include <e-util/e-filter-part.h> #include <e-util/e-filter-rule.h> #include <e-util/e-focus-tracker.h> +#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT #include <e-util/e-html-editor-actions.h> #include <e-util/e-html-editor-cell-dialog.h> #include <e-util/e-html-editor-dialog.h> @@ -121,13 +123,11 @@ #include <e-util/e-html-editor-page-dialog.h> #include <e-util/e-html-editor-paragraph-dialog.h> #include <e-util/e-html-editor-replace-dialog.h> -#include <e-util/e-html-editor-selection.h> #include <e-util/e-html-editor-spell-check-dialog.h> #include <e-util/e-html-editor-table-dialog.h> #include <e-util/e-html-editor-text-dialog.h> -#include <e-util/e-html-editor-utils.h> -#include <e-util/e-html-editor-view.h> #include <e-util/e-html-editor.h> +#endif #include <e-util/e-html-utils.h> #include <e-util/e-icon-factory.h> #include <e-util/e-image-chooser.h> @@ -137,9 +137,11 @@ #include <e-util/e-interval-chooser.h> #include <e-util/e-mail-identity-combo-box.h> #include <e-util/e-mail-signature-combo-box.h> +#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT #include <e-util/e-mail-signature-editor.h> #include <e-util/e-mail-signature-manager.h> #include <e-util/e-mail-signature-preview.h> +#endif #include <e-util/e-mail-signature-script-dialog.h> #include <e-util/e-mail-signature-tree-view.h> #include <e-util/e-map.h> @@ -165,7 +167,9 @@ #include <e-util/e-popup-menu.h> #include <e-util/e-port-entry.h> #include <e-util/e-preferences-window.h> +#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT #include <e-util/e-preview-pane.h> +#endif #include <e-util/e-print.h> #include <e-util/e-printable.h> #include <e-util/e-proxy-combo-box.h> @@ -177,13 +181,16 @@ #include <e-util/e-reflow.h> #include <e-util/e-rule-context.h> #include <e-util/e-rule-editor.h> +#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT #include <e-util/e-search-bar.h> +#endif #include <e-util/e-selectable.h> #include <e-util/e-selection-model-array.h> #include <e-util/e-selection-model-simple.h> #include <e-util/e-selection-model.h> #include <e-util/e-selection.h> #include <e-util/e-send-options.h> +#include <e-util/e-simple-async-result.h> #include <e-util/e-sorter-array.h> #include <e-util/e-sorter.h> #include <e-util/e-source-combo-box.h> @@ -194,6 +201,8 @@ #include <e-util/e-source-selector-dialog.h> #include <e-util/e-source-selector.h> #include <e-util/e-source-util.h> +#include <e-util/e-spell-checker.h> +#include <e-util/e-spell-dictionary.h> #include <e-util/e-spell-entry.h> #include <e-util/e-spell-text-view.h> #include <e-util/e-spinner.h> @@ -248,8 +257,10 @@ #include <e-util/e-url-entry.h> #include <e-util/e-util-enums.h> #include <e-util/e-util-enumtypes.h> +#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT #include <e-util/e-web-view-preview.h> #include <e-util/e-web-view.h> +#endif #include <e-util/e-widget-undo.h> #include <e-util/e-xml-utils.h> #include <e-util/ea-cell-table.h> diff --git a/e-util/e-web-view-preview.c b/e-util/e-web-view-preview.c index 4ad30a0..13fd32d 100644 --- a/e-util/e-web-view-preview.c +++ b/e-util/e-web-view-preview.c @@ -173,7 +173,7 @@ in_scrolled_window (GtkWidget *widget) static void e_web_view_preview_init (EWebViewPreview *preview) { - GtkWidget *tree_view_sw, *web_view_sw; + GtkWidget *tree_view_sw, *web_view; preview->priv = E_WEB_VIEW_PREVIEW_GET_PRIVATE (preview); preview->priv->escape_values = TRUE; @@ -181,13 +181,13 @@ e_web_view_preview_init (EWebViewPreview *preview) gtk_orientable_set_orientation (GTK_ORIENTABLE (preview), GTK_ORIENTATION_VERTICAL); tree_view_sw = in_scrolled_window (gtk_tree_view_new ()); - web_view_sw = in_scrolled_window (e_web_view_new ()); + web_view = e_web_view_new (); gtk_widget_hide (tree_view_sw); - gtk_widget_show (web_view_sw); + gtk_widget_show (web_view); gtk_paned_pack1 (GTK_PANED (preview), tree_view_sw, FALSE, TRUE); - gtk_paned_pack2 (GTK_PANED (preview), web_view_sw, TRUE, TRUE); + gtk_paned_pack2 (GTK_PANED (preview), web_view, TRUE, TRUE); /* rawly 3 lines of a text plus a little bit more */ if (gtk_paned_get_position (GTK_PANED (preview)) < 85) @@ -213,7 +213,7 @@ e_web_view_preview_get_preview (EWebViewPreview *preview) { g_return_val_if_fail (E_IS_WEB_VIEW_PREVIEW (preview), NULL); - return gtk_bin_get_child (GTK_BIN (gtk_paned_get_child2 (GTK_PANED (preview)))); + return gtk_paned_get_child2 (GTK_PANED (preview)); } void @@ -225,7 +225,7 @@ e_web_view_preview_set_preview (EWebViewPreview *preview, g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview)); g_return_if_fail (GTK_IS_WIDGET (preview_widget)); - old_child = gtk_bin_get_child (GTK_BIN (gtk_paned_get_child2 (GTK_PANED (preview)))); + old_child = gtk_paned_get_child2 (GTK_PANED (preview)); if (old_child) { g_return_if_fail (old_child != preview_widget); gtk_widget_destroy (old_child); diff --git a/e-util/e-web-view.c b/e-util/e-web-view.c index 42af88f..011ac27 100644 --- a/e-util/e-web-view.c +++ b/e-util/e-web-view.c @@ -40,18 +40,19 @@ #include "e-selectable.h" #include "e-stock-request.h" +#include <web-extensions/e-web-extension-names.h> + #define E_WEB_VIEW_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_WEB_VIEW, EWebViewPrivate)) -typedef enum { - E_WEB_VIEW_ZOOM_HACK_STATE_NONE, - E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_IN, - E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_OUT -} EWebViewZoomHackState; - typedef struct _AsyncContext AsyncContext; +typedef struct _ElementClickedData { + EWebViewElementClickedFunc callback; + gpointer user_data; +} ElementClickedData; + struct _EWebViewPrivate { GtkUIManager *ui_manager; gchar *selected_uri; @@ -78,17 +79,27 @@ struct _EWebViewPrivate { GHashTable *old_settings; - /* To workaround webkit bug: - * https://bugs.webkit.org/show_bug.cgi?id=89553 */ - EWebViewZoomHackState zoom_hack_state; + GDBusProxy *web_extension; + guint web_extension_watch_name_id; + + WebKitFindController *find_controller; + gulong found_text_handler_id; + gulong failed_to_find_text_handler_id; gboolean has_hover_link; + + GSList *content_requests; /* EContentRequest * */ + + GHashTable *element_clicked_cbs; /* gchar *element_class ~> GPtrArray {ElementClickedData} */ + guint web_extension_element_clicked_signal_id; }; struct _AsyncContext { EActivity *activity; GFile *destination; GInputStream *input_stream; + EContentRequest *content_request; + gchar *uri; }; enum { @@ -99,6 +110,7 @@ enum { PROP_DISABLE_PRINTING, PROP_DISABLE_SAVE_TO_DISK, PROP_OPEN_PROXY, + PROP_PASTE_TARGET_LIST, PROP_PRINT_PROXY, PROP_SAVE_AS_PROXY, PROP_SELECTED_URI @@ -111,11 +123,11 @@ enum { STOP_LOADING, UPDATE_ACTIONS, PROCESS_MAILTO, + URI_REQUESTED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; -static GOnce disable_webkit_3rd_party_plugins_once = G_ONCE_INIT; static const gchar *ui = "<ui>" @@ -161,11 +173,18 @@ G_DEFINE_TYPE_WITH_CODE ( e_web_view_selectable_init)) static void -async_context_free (AsyncContext *async_context) +async_context_free (gpointer ptr) { + AsyncContext *async_context = ptr; + + if (!async_context) + return; + g_clear_object (&async_context->activity); g_clear_object (&async_context->destination); g_clear_object (&async_context->input_stream); + g_clear_object (&async_context->content_request); + g_free (async_context->uri); g_slice_free (AsyncContext, async_context); } @@ -393,38 +412,6 @@ static GtkActionEntry standard_entries[] = { }; static void -web_view_init_web_settings (WebKitWebView *web_view) -{ - WebKitWebSettings *web_settings; - - web_settings = webkit_web_settings_new (); - - g_object_set ( - G_OBJECT (web_settings), - "default-encoding", "UTF-8", - "enable-dns-prefetching", FALSE, - "enable-frame-flattening", TRUE, - "enable-java-applet", FALSE, - "enable-html5-database", FALSE, - "enable-html5-local-storage", FALSE, - "enable-offline-web-application-cache", FALSE, - "enable-site-specific-quirks", TRUE, - "enable-scripts", FALSE, - "respect-image-orientation", TRUE, - NULL); - - e_binding_bind_property ( - web_settings, "enable-caret-browsing", - web_view, "caret-mode", - G_BINDING_BIDIRECTIONAL | - G_BINDING_SYNC_CREATE); - - webkit_web_view_set_settings (web_view, web_settings); - - g_object_unref (web_settings); -} - -static void web_view_menu_item_select_cb (EWebView *web_view, GtkWidget *widget) { @@ -443,20 +430,51 @@ web_view_menu_item_select_cb (EWebView *web_view, } static void +webkit_find_controller_found_text_cb (WebKitFindController *find_controller, + guint match_count, + EWebView *web_view) +{ +} + +static void +webkit_find_controller_failed_to_found_text_cb (WebKitFindController *find_controller, + EWebView *web_view) +{ +} + +static void +web_view_set_find_controller (EWebView *web_view) +{ + WebKitFindController *find_controller; + + find_controller = + webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (web_view)); + + web_view->priv->found_text_handler_id = g_signal_connect ( + find_controller, "found-text", + G_CALLBACK (webkit_find_controller_found_text_cb), web_view); + + web_view->priv->failed_to_find_text_handler_id = g_signal_connect ( + find_controller, "failed-to-find-text", + G_CALLBACK (webkit_find_controller_failed_to_found_text_cb), web_view); + + web_view->priv->find_controller = find_controller; +} + +static void web_view_update_document_highlights (EWebView *web_view) { - WebKitWebView *webkit_web_view; GList *head, *link; - webkit_web_view = WEBKIT_WEB_VIEW (web_view); - head = g_queue_peek_head_link (&web_view->priv->highlights); - for (link = head; link != NULL; link = g_list_next (link)) - webkit_web_view_mark_text_matches ( - webkit_web_view, link->data, FALSE, 0); - - webkit_web_view_set_highlight_text_matches (webkit_web_view, TRUE); + for (link = head; link != NULL; link = g_list_next (link)) { + webkit_find_controller_search ( + web_view->priv->find_controller, + link->data, + WEBKIT_FIND_OPTIONS_NONE, + G_MAXUINT); + } } static void @@ -484,9 +502,10 @@ web_view_connect_proxy_cb (EWebView *web_view, static gboolean web_view_context_menu_cb (WebKitWebView *webkit_web_view, - GtkWidget *default_menu, + WebKitContextMenu *context_menu, + GdkEvent *event, WebKitHitTestResult *hit_test_result, - gboolean triggered_with_keyboard) + gpointer user_data) { WebKitHitTestResultContext context; EWebView *web_view; @@ -501,7 +520,7 @@ web_view_context_menu_cb (WebKitWebView *webkit_web_view, if (hit_test_result == NULL) return FALSE; - g_object_get (hit_test_result, "context", &context, NULL); + context = webkit_hit_test_result_get_context (hit_test_result); if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) { gchar *image_uri = NULL; @@ -527,30 +546,17 @@ web_view_context_menu_cb (WebKitWebView *webkit_web_view, return event_handled; } -static GtkWidget * -web_view_create_plugin_widget_cb (EWebView *web_view, - const gchar *mime_type, - const gchar *uri, - GHashTable *param) -{ - EWebViewClass *class; - - /* XXX WebKitWebView does not provide a class method for - * this signal, so we do so we can override the default - * behavior from subclasses for special URI types. */ - - class = E_WEB_VIEW_GET_CLASS (web_view); - g_return_val_if_fail (class->create_plugin_widget != NULL, NULL); - - return class->create_plugin_widget (web_view, mime_type, uri, param); -} - static void -web_view_hovering_over_link_cb (EWebView *web_view, - const gchar *title, - const gchar *uri) +web_view_mouse_target_changed_cb (EWebView *web_view, + WebKitHitTestResult *hit_test_result, + guint modifiers, + gpointer user_data) { EWebViewClass *class; + const gchar *title, *uri; + + title = webkit_hit_test_result_get_link_title (hit_test_result); + uri = webkit_hit_test_result_get_link_uri (hit_test_result); web_view->priv->has_hover_link = uri && *uri; @@ -565,55 +571,60 @@ web_view_hovering_over_link_cb (EWebView *web_view, } static gboolean -web_view_navigation_policy_decision_requested_cb (EWebView *web_view, - WebKitWebFrame *frame, - WebKitNetworkRequest *request, - WebKitWebNavigationAction *navigation_action, - WebKitWebPolicyDecision *policy_decision) +web_view_decide_policy_cb (EWebView *web_view, + WebKitPolicyDecision *decision, + WebKitPolicyDecisionType type) { EWebViewClass *class; - WebKitWebNavigationReason reason; - const gchar *uri, *frame_uri; + WebKitNavigationPolicyDecision *navigation_decision; + WebKitNavigationAction *navigation_action; + WebKitNavigationType navigation_type; + WebKitURIRequest *request; + const gchar *uri, *view_uri; + + if (type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION && + type != WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION) + return FALSE; + + navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION (decision); + navigation_action = webkit_navigation_policy_decision_get_navigation_action (navigation_decision); + navigation_type = webkit_navigation_action_get_navigation_type (navigation_action); - reason = webkit_web_navigation_action_get_reason (navigation_action); - if (reason != WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) + if (navigation_type != WEBKIT_NAVIGATION_TYPE_LINK_CLICKED) return FALSE; - uri = webkit_network_request_get_uri (request); - frame_uri = webkit_web_frame_get_uri (frame); + request = webkit_navigation_action_get_request (navigation_action); + uri = webkit_uri_request_get_uri (request); + view_uri = webkit_web_view_get_uri (WEBKIT_WEB_VIEW (web_view)); /* Allow navigation through fragments in page */ - if (uri && *uri && frame_uri && *frame_uri) { - SoupURI *uri_link, *uri_frame; + if (uri && *uri && view_uri && *view_uri) { + SoupURI *uri_link, *uri_view; uri_link = soup_uri_new (uri); - uri_frame = soup_uri_new (frame_uri); - if (uri_link && uri_frame) { + uri_view = soup_uri_new (view_uri); + if (uri_link && uri_view) { const gchar *tmp1, *tmp2; tmp1 = soup_uri_get_scheme (uri_link); - tmp2 = soup_uri_get_scheme (uri_frame); + tmp2 = soup_uri_get_scheme (uri_view); /* The scheme on both URIs should be the same */ - if (tmp1 && tmp2) { - if (g_ascii_strcasecmp (tmp1, tmp2) != 0) - goto free_uris; - } + if (tmp1 && tmp2 && g_ascii_strcasecmp (tmp1, tmp2) != 0) + goto free_uris; tmp1 = soup_uri_get_host (uri_link); - tmp2 = soup_uri_get_host (uri_frame); + tmp2 = soup_uri_get_host (uri_view); /* The host on both URIs should be the same */ - if (tmp1 && tmp2) { - if (g_ascii_strcasecmp (tmp1, tmp2) != 0) - goto free_uris; - } + if (tmp1 && tmp2 && g_ascii_strcasecmp (tmp1, tmp2) != 0) + goto free_uris; /* URI from link should have fragment set - could be empty */ if (soup_uri_get_fragment (uri_link)) { soup_uri_free (uri_link); - soup_uri_free (uri_frame); - webkit_web_policy_decision_use (policy_decision); + soup_uri_free (uri_view); + webkit_policy_decision_use (decision); return TRUE; } } @@ -621,8 +632,8 @@ web_view_navigation_policy_decision_requested_cb (EWebView *web_view, free_uris: if (uri_link) soup_uri_free (uri_link); - if (uri_frame) - soup_uri_free (uri_frame); + if (uri_view) + soup_uri_free (uri_view); } /* XXX WebKitWebView does not provide a class method for @@ -632,7 +643,7 @@ web_view_navigation_policy_decision_requested_cb (EWebView *web_view, class = E_WEB_VIEW_GET_CLASS (web_view); g_return_val_if_fail (class->link_clicked != NULL, FALSE); - webkit_web_policy_decision_ignore (policy_decision); + webkit_policy_decision_ignore (decision); class->link_clicked (web_view, uri); @@ -665,7 +676,7 @@ style_updated_cb (EWebView *web_view) e_web_view_add_css_rule_into_style_sheet ( web_view, - "-e-web-view-css-sheet", + "-e-web-view-style-sheet", ".-e-web-view-background-color", style); @@ -684,7 +695,7 @@ style_updated_cb (EWebView *web_view) e_web_view_add_css_rule_into_style_sheet ( web_view, - "-e-web-view-css-sheet", + "-e-web-view-style-sheet", ".-e-web-view-text-color", style); @@ -693,60 +704,63 @@ style_updated_cb (EWebView *web_view) } static void -web_view_load_status_changed_cb (WebKitWebView *webkit_web_view, - GParamSpec *pspec, - gpointer user_data) +web_view_load_changed_cb (WebKitWebView *webkit_web_view, + WebKitLoadEvent load_event, + gpointer user_data) { - WebKitLoadStatus status; EWebView *web_view; web_view = E_WEB_VIEW (webkit_web_view); - status = webkit_web_view_get_load_status (webkit_web_view); - - if (web_view->priv->zoom_hack_state == E_WEB_VIEW_ZOOM_HACK_STATE_NONE && - status == WEBKIT_LOAD_COMMITTED) { - if (webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)) > 0.9999) { - e_web_view_zoom_out (web_view); - web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_OUT; - } else { - e_web_view_zoom_in (web_view); - web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_IN; - } - } else if (web_view->priv->zoom_hack_state != E_WEB_VIEW_ZOOM_HACK_STATE_NONE && - status == WEBKIT_LOAD_FAILED) { - if (web_view->priv->zoom_hack_state == E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_IN) - e_web_view_zoom_out (web_view); - else - e_web_view_zoom_in (web_view); - - web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_NONE; - } + if (load_event == WEBKIT_LOAD_STARTED) + g_hash_table_remove_all (web_view->priv->element_clicked_cbs); - if (status != WEBKIT_LOAD_FINISHED) + if (load_event != WEBKIT_LOAD_FINISHED) return; style_updated_cb (web_view); web_view_update_document_highlights (web_view); +} - if (web_view->priv->zoom_hack_state == E_WEB_VIEW_ZOOM_HACK_STATE_NONE) { - /* This may not happen, but just in case keep it here. */ - if (webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)) > 0.9999) { - e_web_view_zoom_out (web_view); - e_web_view_zoom_in (web_view); - } else { - e_web_view_zoom_in (web_view); - e_web_view_zoom_out (web_view); - } - } else { - if (web_view->priv->zoom_hack_state == E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_IN) - e_web_view_zoom_out (web_view); - else - e_web_view_zoom_in (web_view); +static GObjectConstructParam* +find_property (guint n_properties, + GObjectConstructParam* properties, + GParamSpec* param_spec) +{ + while (n_properties--) { + if (properties->pspec == param_spec) + return properties; + properties++; + } + + return NULL; +} - web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_NONE; +static GObject* +web_view_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObjectClass* object_class; + GParamSpec* param_spec; + GObjectConstructParam *param = NULL; + + object_class = G_OBJECT_CLASS (g_type_class_ref(type)); + g_return_val_if_fail (object_class != NULL, NULL); + + if (construct_properties && n_construct_properties != 0) { + param_spec = g_object_class_find_property (object_class, "settings"); + if ((param = find_property (n_construct_properties, construct_properties, param_spec))) + g_value_take_object (param->value, e_web_view_get_default_webkit_settings ()); + param_spec = g_object_class_find_property(object_class, "user-content-manager"); + if ((param = find_property (n_construct_properties, construct_properties, param_spec))) + g_value_take_object (param->value, webkit_user_content_manager_new ()); } + + g_type_class_unref (object_class); + + return G_OBJECT_CLASS (e_web_view_parent_class)->constructor(type, n_construct_properties, construct_properties); } static void @@ -762,6 +776,11 @@ web_view_set_property (GObject *object, g_value_get_boolean (value)); return; + case PROP_COPY_TARGET_LIST: + /* This is a fake property. */ + g_warning ("%s: EWebView::copy-target-list not used", G_STRFUNC); + return; + case PROP_CURSOR_IMAGE_SRC: e_web_view_set_cursor_image_src ( E_WEB_VIEW (object), @@ -786,6 +805,11 @@ web_view_set_property (GObject *object, g_value_get_object (value)); return; + case PROP_PASTE_TARGET_LIST: + /* This is a fake property. */ + g_warning ("%s: EWebView::paste-target-list not used", G_STRFUNC); + return; + case PROP_PRINT_PROXY: e_web_view_set_print_proxy ( E_WEB_VIEW (object), @@ -820,6 +844,11 @@ web_view_get_property (GObject *object, E_WEB_VIEW (object))); return; + case PROP_COPY_TARGET_LIST: + /* This is a fake property. */ + g_value_set_boxed (value, NULL); + return; + case PROP_CURSOR_IMAGE_SRC: g_value_set_string ( value, e_web_view_get_cursor_image_src ( @@ -844,6 +873,11 @@ web_view_get_property (GObject *object, E_WEB_VIEW (object))); return; + case PROP_PASTE_TARGET_LIST: + /* This is a fake property. */ + g_value_set_boxed (value, NULL); + return; + case PROP_PRINT_PROXY: g_value_set_object ( value, e_web_view_get_print_proxy ( @@ -895,12 +929,44 @@ web_view_dispose (GObject *object) priv->antialiasing_changed_handler_id = 0; } + if (priv->web_extension_watch_name_id > 0) { + g_bus_unwatch_name (priv->web_extension_watch_name_id); + priv->web_extension_watch_name_id = 0; + } + + if (priv->found_text_handler_id > 0) { + g_signal_handler_disconnect ( + priv->find_controller, + priv->found_text_handler_id); + priv->found_text_handler_id = 0; + } + + if (priv->failed_to_find_text_handler_id > 0) { + g_signal_handler_disconnect ( + priv->find_controller, + priv->failed_to_find_text_handler_id); + priv->failed_to_find_text_handler_id = 0; + } + + if (priv->web_extension && priv->web_extension_element_clicked_signal_id) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection (priv->web_extension), + priv->web_extension_element_clicked_signal_id); + priv->web_extension_element_clicked_signal_id = 0; + } + + g_hash_table_remove_all (priv->element_clicked_cbs); + + g_slist_free_full (priv->content_requests, g_object_unref); + priv->content_requests = NULL; + g_clear_object (&priv->ui_manager); g_clear_object (&priv->open_proxy); g_clear_object (&priv->print_proxy); g_clear_object (&priv->save_as_proxy); g_clear_object (&priv->aliasing_settings); g_clear_object (&priv->font_settings); + g_clear_object (&priv->web_extension); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_web_view_parent_class)->dispose (object); @@ -924,13 +990,149 @@ web_view_finalize (GObject *object) priv->old_settings = NULL; } + g_hash_table_destroy (priv->element_clicked_cbs); + /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_web_view_parent_class)->finalize (object); } + +static void +web_view_uri_request_done_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + WebKitURISchemeRequest *request = user_data; + GInputStream *stream = NULL; + gint64 stream_length = -1; + gchar *mime_type = NULL; + GError *error = NULL; + + g_return_if_fail (E_IS_CONTENT_REQUEST (source_object)); + g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request)); + + if (!e_content_request_process_finish (E_CONTENT_REQUEST (source_object), + result, &stream, &stream_length, &mime_type, &error)) { + webkit_uri_scheme_request_finish_error (request, error); + } else { + webkit_uri_scheme_request_finish (request, stream, stream_length, mime_type); + + g_clear_object (&stream); + g_free (mime_type); + } + + g_object_unref (request); +} + +static void +web_view_process_uri_request_cb (WebKitURISchemeRequest *request, + gpointer user_data) +{ + EContentRequest *content_request = user_data; + const gchar *uri; + gchar *redirect_to_uri = NULL; + GObject *requester; + + g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request)); + g_return_if_fail (E_IS_CONTENT_REQUEST (content_request)); + + uri = webkit_uri_scheme_request_get_uri (request); + requester = G_OBJECT (webkit_uri_scheme_request_get_web_view (request)); + + g_return_if_fail (e_content_request_can_process_uri (content_request, uri)); + + if (E_IS_WEB_VIEW (requester)) { + /* Expects an empty string to abandon the request, + or NULL to keep the passed-in uri, + or a new uri to load instead. */ + g_signal_emit (requester, signals[URI_REQUESTED], 0, uri, &redirect_to_uri); + + if (redirect_to_uri && *redirect_to_uri) { + uri = redirect_to_uri; + } else if (redirect_to_uri) { + GError *error; + + g_free (redirect_to_uri); + + error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled"); + + webkit_uri_scheme_request_finish_error (request, error); + return; + } + } + + e_content_request_process (content_request, uri, requester, NULL, + web_view_uri_request_done_cb, g_object_ref (request)); + + g_free (redirect_to_uri); +} + +/* 'scheme' is like "file", not "file:" */ +void +e_web_view_register_content_request_for_scheme (EWebView *web_view, + const gchar *scheme, + EContentRequest *content_request) +{ + WebKitWebContext *web_context; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (E_IS_CONTENT_REQUEST (content_request)); + g_return_if_fail (scheme != NULL); + + web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (web_view)); + + webkit_web_context_register_uri_scheme (web_context, scheme, web_view_process_uri_request_cb, + g_object_ref (content_request), g_object_unref); + + if (!g_slist_find (web_view->priv->content_requests, content_request)) { + web_view->priv->content_requests = g_slist_prepend ( + web_view->priv->content_requests, + g_object_ref (content_request)); + } +} + +static void +web_view_initialize (WebKitWebView *web_view) +{ + WebKitWebContext *web_context; + EContentRequest *content_request; + const gchar *id = "org.gnome.settings-daemon.plugins.xsettings"; + GSettings *settings = NULL, *font_settings; + GSettingsSchema *settings_schema; + + web_context = webkit_web_view_get_context (web_view); + + webkit_web_context_set_cache_model (web_context, WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); + + content_request = e_file_request_new (); + e_web_view_register_content_request_for_scheme (E_WEB_VIEW (web_view), "evo-file", content_request); + g_object_unref (content_request); + + content_request = e_stock_request_new (); + e_web_view_register_content_request_for_scheme (E_WEB_VIEW (web_view), "gtk-stock", content_request); + g_object_unref (content_request); + + /* Optional schema */ + settings_schema = g_settings_schema_source_lookup ( + g_settings_schema_source_get_default (), id, FALSE); + + if (settings_schema) + settings = e_util_ref_settings (id); + + font_settings = e_util_ref_settings ("org.gnome.desktop.interface"); + e_web_view_update_fonts_settings ( + font_settings, settings, NULL, NULL, GTK_WIDGET (web_view)); + + g_object_unref (font_settings); + if (settings) + g_object_unref (settings); +} + + static void web_view_constructed (GObject *object) { + WebKitSettings *web_settings; #ifndef G_OS_WIN32 GSettings *settings; @@ -953,6 +1155,23 @@ web_view_constructed (GObject *object) /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_web_view_parent_class)->constructed (object); + + web_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (object)); + + g_object_set ( + G_OBJECT (web_settings), + "default-charset", "UTF-8", + NULL); + + e_binding_bind_property ( + web_settings, "enable-caret-browsing", + E_WEB_VIEW (object), "caret-mode", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + + web_view_initialize (WEBKIT_WEB_VIEW (object)); + + web_view_set_find_controller (E_WEB_VIEW (object)); } static gboolean @@ -992,7 +1211,8 @@ web_view_scroll_event (GtkWidget *widget, } } - return FALSE; + return GTK_WIDGET_CLASS (e_web_view_parent_class)-> + scroll_event (widget, event); } static gboolean @@ -1146,9 +1366,8 @@ web_view_load_string (EWebView *web_view, if (string == NULL) string = ""; - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (web_view), - string, "text/html", "UTF-8", "evo-file:///"); + webkit_web_view_load_html ( + WEBKIT_WEB_VIEW (web_view), string, "evo-file:///"); } static void @@ -1162,13 +1381,6 @@ web_view_load_uri (EWebView *web_view, } static gchar * -web_view_redirect_uri (EWebView *web_view, - const gchar *uri) -{ - return g_strdup (uri); -} - -static gchar * web_view_suggest_filename (EWebView *web_view, const gchar *uri) { @@ -1204,8 +1416,164 @@ web_view_stop_loading (EWebView *web_view) } static void -web_view_update_actions (EWebView *web_view) +web_view_register_element_clicked_hfunc (gpointer key, + gpointer value, + gpointer user_data) +{ + const gchar *elem_class = key; + EWebView *web_view = user_data; + + g_return_if_fail (elem_class && *elem_class); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + if (!web_view->priv->web_extension) + return; + + g_dbus_proxy_call ( + web_view->priv->web_extension, + "RegisterElementClicked", + g_variant_new ( + "(ts)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)), + elem_class), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +web_view_element_clicked_signal_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) { + EWebView *web_view = user_data; + const gchar *elem_class = NULL, *elem_value = NULL; + GtkAllocation elem_position; + guint64 page_id = 0; + gint position_left = 0, position_top = 0, position_width = 0, position_height = 0; + GPtrArray *listeners; + + if (g_strcmp0 (signal_name, "ElementClicked") != 0) + return; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + if (!parameters) + return; + + g_variant_get (parameters, "(t&s&siiii)", &page_id, &elem_class, &elem_value, &position_left, &position_top, &position_width, &position_height); + + if (!elem_class || !*elem_class || page_id != webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view))) + return; + + elem_position.x = position_left; + elem_position.y = position_top; + elem_position.width = position_width; + elem_position.height = position_height; + + listeners = g_hash_table_lookup (web_view->priv->element_clicked_cbs, elem_class); + if (listeners) { + guint ii; + + for (ii = 0; ii <listeners->len; ii++) { + ElementClickedData *ecd = g_ptr_array_index (listeners, ii); + + if (ecd && ecd->callback) + ecd->callback (web_view, elem_class, elem_value, &elem_position, ecd->user_data); + } + } +} + +static void +web_extension_proxy_created_cb (GDBusProxy *proxy, + GAsyncResult *result, + EWebView *web_view) +{ + GError *error = NULL; + + web_view->priv->web_extension = g_dbus_proxy_new_finish (result, &error); + if (!web_view->priv->web_extension) { + g_warning ("Error creating web extension proxy: %s\n", error->message); + g_error_free (error); + } else { + web_view->priv->web_extension_element_clicked_signal_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (web_view->priv->web_extension), + g_dbus_proxy_get_name (web_view->priv->web_extension), + E_WEB_EXTENSION_INTERFACE, + "ElementClicked", + E_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + web_view_element_clicked_signal_cb, + web_view, + NULL); + + g_hash_table_foreach (web_view->priv->element_clicked_cbs, web_view_register_element_clicked_hfunc, web_view); + } +} + +static void +web_extension_appeared_cb (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + EWebView *web_view) +{ + g_dbus_proxy_new ( + connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + name, + E_WEB_EXTENSION_OBJECT_PATH, + E_WEB_EXTENSION_INTERFACE, + NULL, + (GAsyncReadyCallback) web_extension_proxy_created_cb, + web_view); +} + +static void +web_extension_vanished_cb (GDBusConnection *connection, + const gchar *name, + EWebView *web_view) +{ + g_clear_object (&web_view->priv->web_extension); +} + +static void +web_view_watch_web_extension (EWebView *web_view) +{ + web_view->priv->web_extension_watch_name_id = + g_bus_watch_name ( + G_BUS_TYPE_SESSION, + E_WEB_EXTENSION_SERVICE_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + (GBusNameAppearedCallback) web_extension_appeared_cb, + (GBusNameVanishedCallback) web_extension_vanished_cb, + web_view, + NULL); +} + +GDBusProxy * +e_web_view_get_web_extension_proxy (EWebView *web_view) +{ + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + + return web_view->priv->web_extension; +} + +static void +web_view_update_actions_cb (WebKitWebView *webkit_web_view, + GAsyncResult *result, + gpointer user_data) +{ + EWebView *web_view; GtkActionGroup *action_group; gboolean can_copy; gboolean scheme_is_http = FALSE; @@ -1216,8 +1584,11 @@ web_view_update_actions (EWebView *web_view) const gchar *group_name; const gchar *uri; + web_view = E_WEB_VIEW (webkit_web_view); + uri = e_web_view_get_selected_uri (web_view); - can_copy = webkit_web_view_can_copy_clipboard (WEBKIT_WEB_VIEW (web_view)); + can_copy = webkit_web_view_can_execute_editing_command_finish ( + webkit_web_view, result, NULL); cursor_image_src = e_web_view_get_cursor_image_src (web_view); /* Parse the URI early so we know if the actions will work. */ @@ -1301,6 +1672,17 @@ web_view_update_actions (EWebView *web_view) } static void +web_view_update_actions (EWebView *web_view) +{ + webkit_web_view_can_execute_editing_command ( + WEBKIT_WEB_VIEW (web_view), + WEBKIT_EDITING_COMMAND_COPY, + NULL, /* cancellable */ + (GAsyncReadyCallback) web_view_update_actions_cb, + NULL); +} + +static void web_view_submit_alert (EAlertSink *alert_sink, EAlert *alert) { @@ -1397,6 +1779,19 @@ web_view_submit_alert (EAlertSink *alert_sink, } static void +web_view_can_execute_editing_command_cb (WebKitWebView *webkit_web_view, + GAsyncResult *result, + GtkAction *action) +{ + gboolean can_do_command; + + can_do_command = webkit_web_view_can_execute_editing_command_finish ( + webkit_web_view, result, NULL); + + gtk_action_set_sensitive (action, can_do_command); +} + +static void web_view_selectable_update_actions (ESelectable *selectable, EFocusTracker *focus_tracker, GdkAtom *clipboard_targets, @@ -1410,21 +1805,33 @@ web_view_selectable_update_actions (ESelectable *selectable, web_view = WEBKIT_WEB_VIEW (selectable); action = e_focus_tracker_get_cut_clipboard_action (focus_tracker); - sensitive = webkit_web_view_can_cut_clipboard (web_view); + webkit_web_view_can_execute_editing_command ( + WEBKIT_WEB_VIEW (web_view), + WEBKIT_EDITING_COMMAND_CUT, + NULL, /* cancellable */ + (GAsyncReadyCallback) web_view_can_execute_editing_command_cb, + action); tooltip = _("Cut the selection"); - gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_copy_clipboard_action (focus_tracker); - sensitive = webkit_web_view_can_copy_clipboard (web_view); + webkit_web_view_can_execute_editing_command ( + WEBKIT_WEB_VIEW (web_view), + WEBKIT_EDITING_COMMAND_COPY, + NULL, /* cancellable */ + (GAsyncReadyCallback) web_view_can_execute_editing_command_cb, + action); tooltip = _("Copy the selection"); - gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_paste_clipboard_action (focus_tracker); - sensitive = webkit_web_view_can_paste_clipboard (web_view); + webkit_web_view_can_execute_editing_command ( + WEBKIT_WEB_VIEW (web_view), + WEBKIT_EDITING_COMMAND_PASTE, + NULL, /* cancellable */ + (GAsyncReadyCallback) web_view_can_execute_editing_command_cb, + action); tooltip = _("Paste the clipboard"); - gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_select_all_action (focus_tracker); @@ -1480,30 +1887,6 @@ e_web_view_test_change_and_update_fonts_cb (EWebView *web_view, } } -static gpointer -web_view_disable_webkit_3rd_party_plugins (gpointer unused) -{ - WebKitWebPluginDatabase *database; - GSList *installed_plugins, *iterator; - - database = webkit_get_web_plugin_database (); - - if (!database) - return NULL; - - installed_plugins = webkit_web_plugin_database_get_plugins (database); - - if (!installed_plugins) - return NULL; - - for (iterator = installed_plugins; iterator; iterator = iterator->next) - webkit_web_plugin_set_enabled (iterator->data, FALSE); - - webkit_web_plugin_database_plugins_list_free (installed_plugins); - - return NULL; -} - static void web_view_toplevel_event_after_cb (GtkWidget *widget, GdkEvent *event, @@ -1564,6 +1947,7 @@ e_web_view_class_init (EWebViewClass *class) g_type_class_add_private (class, sizeof (EWebViewPrivate)); object_class = G_OBJECT_CLASS (class); + object_class->constructor = web_view_constructor; object_class->set_property = web_view_set_property; object_class->get_property = web_view_get_property; object_class->dispose = web_view_dispose; @@ -1581,7 +1965,6 @@ e_web_view_class_init (EWebViewClass *class) class->link_clicked = web_view_link_clicked; class->load_string = web_view_load_string; class->load_uri = web_view_load_uri; - class->redirect_uri = web_view_redirect_uri; class->suggest_filename = web_view_suggest_filename; class->popup_event = web_view_popup_event; class->stop_loading = web_view_stop_loading; @@ -1597,6 +1980,18 @@ e_web_view_class_init (EWebViewClass *class) FALSE, G_PARAM_READWRITE)); + /* Inherited from ESelectableInterface; just a fake property here */ + g_object_class_override_property ( + object_class, + PROP_COPY_TARGET_LIST, + "copy-target-list"); + + /* Inherited from ESelectableInterface; just a fake property here */ + g_object_class_override_property ( + object_class, + PROP_PASTE_TARGET_LIST, + "paste-target-list"); + g_object_class_install_property ( object_class, PROP_CURSOR_IMAGE_SRC, @@ -1726,9 +2121,16 @@ e_web_view_class_init (EWebViewClass *class) e_marshal_BOOLEAN__STRING, G_TYPE_BOOLEAN, 1, G_TYPE_STRING); - webkit_set_cache_model (WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); - webkit_set_default_web_database_quota (0); - webkit_application_cache_set_maximum_size (0); + /* Expects an empty string to abandon the request, + or NULL to keep the passed-in uri, + or a new uri to load instead. */ + signals[URI_REQUESTED] = g_signal_new ( + "uri-requested", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EWebViewClass, uri_requested), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER); } static void @@ -1748,6 +2150,14 @@ e_web_view_selectable_init (ESelectableInterface *iface) } static void +initialize_web_extensions_cb (WebKitWebContext *web_context) +{ + /* Set the web extensions dir before the process is launched */ + webkit_web_context_set_web_extensions_directory ( + web_context, EVOLUTION_WEB_EXTENSIONS_DIR); +} + +static void e_web_view_init (EWebView *web_view) { GtkUIManager *ui_manager; @@ -1760,48 +2170,35 @@ e_web_view_init (EWebView *web_view) gulong handler_id; GError *error = NULL; - g_once ( - &disable_webkit_3rd_party_plugins_once, - web_view_disable_webkit_3rd_party_plugins, NULL); - web_view->priv = E_WEB_VIEW_GET_PRIVATE (web_view); web_view->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); - web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_NONE; - - /* XXX No WebKitWebView class method pointers to - * override so we have to use signal handlers. */ - - g_signal_connect ( - web_view, "create-plugin-widget", - G_CALLBACK (web_view_create_plugin_widget_cb), NULL); g_signal_connect ( web_view, "context-menu", G_CALLBACK (web_view_context_menu_cb), NULL); g_signal_connect ( - web_view, "hovering-over-link", - G_CALLBACK (web_view_hovering_over_link_cb), NULL); + web_view, "mouse-target-changed", + G_CALLBACK (web_view_mouse_target_changed_cb), NULL); g_signal_connect ( - web_view, "navigation-policy-decision-requested", - G_CALLBACK (web_view_navigation_policy_decision_requested_cb), + web_view, "decide-policy", + G_CALLBACK (web_view_decide_policy_cb), NULL); g_signal_connect ( - web_view, "new-window-policy-decision-requested", - G_CALLBACK (web_view_navigation_policy_decision_requested_cb), - NULL); + webkit_web_context_get_default (), "initialize-web-extensions", + G_CALLBACK (initialize_web_extensions_cb), NULL); g_signal_connect ( + web_view, "load-changed", + G_CALLBACK (web_view_load_changed_cb), NULL); +/* FIXME WK2 + g_signal_connect ( web_view, "document-load-finished", G_CALLBACK (style_updated_cb), NULL); - - e_signal_connect_notify ( - web_view, "notify::load-status", - G_CALLBACK (web_view_load_status_changed_cb), NULL); - +*/ g_signal_connect ( web_view, "style-updated", G_CALLBACK (style_updated_cb), NULL); @@ -1817,10 +2214,7 @@ e_web_view_init (EWebView *web_view) ui_manager, "connect-proxy", G_CALLBACK (web_view_connect_proxy_cb), web_view); - web_view_init_web_settings (WEBKIT_WEB_VIEW (web_view)); - - e_web_view_install_request_handler (web_view, E_TYPE_FILE_REQUEST); - e_web_view_install_request_handler (web_view, E_TYPE_STOCK_REQUEST); + web_view_watch_web_extension (web_view); settings = e_util_ref_settings ("org.gnome.desktop.interface"); web_view->priv->font_settings = g_object_ref (settings); @@ -1849,8 +2243,6 @@ e_web_view_init (EWebView *web_view) g_settings_schema_unref (settings_schema); } - e_web_view_update_fonts (web_view); - action_group = gtk_action_group_new ("uri"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); @@ -1958,13 +2350,15 @@ e_web_view_init (EWebView *web_view) e_plugin_ui_register_manager (ui_manager, id, web_view); e_plugin_ui_enable_manager (ui_manager, id); - e_web_view_clear (E_WEB_VIEW (web_view)); + web_view->priv->element_clicked_cbs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); } GtkWidget * e_web_view_new (void) { - return g_object_new (E_TYPE_WEB_VIEW, NULL); + return g_object_new ( + E_TYPE_WEB_VIEW, + NULL); } void @@ -1972,7 +2366,7 @@ e_web_view_clear (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - webkit_web_view_load_html_string ( + webkit_web_view_load_html ( WEBKIT_WEB_VIEW (web_view), "<html>" "<head></head>" @@ -2010,37 +2404,6 @@ e_web_view_load_uri (EWebView *web_view, } /** - * e_web_view_redirect_uri: - * @web_view: an #EWebView - * @uri: the requested URI - * - * Replaces @uri with a redirected URI as necessary, primarily for use - * with custom #SoupRequest handlers. Typically this function would be - * called just prior to handing a request off to a #SoupSession, such as - * from a #WebKitWebView #WebKitWebView::resource-request-starting signal - * handler. - * - * A newly-allocated URI string is always returned, whether the @uri was - * redirected or not. Free the returned string with g_free(). - * - * Returns: the redirected URI or a copy of @uri - **/ -gchar * -e_web_view_redirect_uri (EWebView *web_view, - const gchar *uri) -{ - EWebViewClass *class; - - g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - g_return_val_if_fail (uri != NULL, NULL); - - class = E_WEB_VIEW_GET_CLASS (web_view); - g_return_val_if_fail (class->redirect_uri != NULL, NULL); - - return class->redirect_uri (web_view, uri); -} - -/** * e_web_view_suggest_filename: * @web_view: an #EWebView * @uri: a URI string @@ -2089,19 +2452,101 @@ e_web_view_reload (EWebView *web_view) webkit_web_view_reload (WEBKIT_WEB_VIEW (web_view)); } +static void +get_document_content_html_cb (GDBusProxy *web_extension, + GAsyncResult *result, + GTask *task) +{ + GVariant *result_variant; + gchar *html_content = NULL; + + result_variant = g_dbus_proxy_call_finish (web_extension, result, NULL); + if (result_variant) + g_variant_get (result_variant, "(s)", &html_content); + g_variant_unref (result_variant); + + g_task_return_pointer (task, html_content, g_free); + g_object_unref (task); +} + +void +e_web_view_get_content_html (EWebView *web_view, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusProxy *web_extension; + GTask *task; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + task = g_task_new (web_view, cancellable, callback, user_data); + + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (web_extension) { + g_dbus_proxy_call ( + web_extension, + "GetDocumentContentHTML", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (web_view))), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + (GAsyncReadyCallback) get_document_content_html_cb, + g_object_ref (task)); + } else + g_task_return_pointer (task, NULL, NULL); +} + gchar * -e_web_view_get_html (EWebView *web_view) +e_web_view_get_content_html_finish (EWebView *web_view, + GAsyncResult *result, + GError **error) { - WebKitDOMDocument *document; - WebKitDOMElement *element; + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + g_return_val_if_fail (g_task_is_valid (result, web_view), FALSE); + + return g_task_propagate_pointer (G_TASK (result), error); +} + +gchar * +e_web_view_get_content_html_sync (EWebView *web_view, + GCancellable *cancellable, + GError **error) +{ + GDBusProxy *web_extension; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view)); - element = webkit_dom_document_get_document_element (document); + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (web_extension) { + GVariant *result; + + result = g_dbus_proxy_call_sync ( + web_extension, + "GetDocumentContentHTML", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (web_view))), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + + if (result) { + gchar *html_content = NULL; + + g_variant_get (result, "(s)", &html_content); + g_variant_unref (result); + + return html_content; + } + } - return webkit_dom_html_element_get_outer_html ( - WEBKIT_DOM_HTML_ELEMENT (element)); + return NULL; } gboolean @@ -2131,8 +2576,10 @@ e_web_view_get_copy_target_list (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - return webkit_web_view_get_copy_target_list ( - WEBKIT_WEB_VIEW (web_view)); + return NULL; + /* FIXME WK2 */ + /*return webkit_web_view_get_copy_target_list ( + WEBKIT_WEB_VIEW (web_view));*/ } gboolean @@ -2184,7 +2631,7 @@ e_web_view_get_editable (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - return webkit_web_view_get_editable (WEBKIT_WEB_VIEW (web_view)); + return webkit_web_view_is_editable (WEBKIT_WEB_VIEW (web_view)); } void @@ -2277,8 +2724,10 @@ e_web_view_get_paste_target_list (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + /* FIXME WK2 return webkit_web_view_get_paste_target_list ( - WEBKIT_WEB_VIEW (web_view)); + WEBKIT_WEB_VIEW (web_view)); */ + return NULL; } GtkAction * @@ -2352,11 +2801,11 @@ e_web_view_add_highlight (EWebView *web_view, &web_view->priv->highlights, g_strdup (highlight)); - webkit_web_view_mark_text_matches ( - WEBKIT_WEB_VIEW (web_view), highlight, FALSE, 0); - - webkit_web_view_set_highlight_text_matches ( - WEBKIT_WEB_VIEW (web_view), TRUE); + webkit_find_controller_search ( + web_view->priv->find_controller, + highlight, + WEBKIT_FIND_OPTIONS_NONE, + G_MAXUINT); } void @@ -2364,7 +2813,7 @@ e_web_view_clear_highlights (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (web_view)); + webkit_find_controller_search_finish (web_view->priv->find_controller); while (!g_queue_is_empty (&web_view->priv->highlights)) g_free (g_queue_pop_head (&web_view->priv->highlights)); @@ -2411,7 +2860,8 @@ e_web_view_copy_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (web_view)); + webkit_web_view_execute_editing_command ( + WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_COPY); } void @@ -2419,15 +2869,43 @@ e_web_view_cut_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - webkit_web_view_cut_clipboard (WEBKIT_WEB_VIEW (web_view)); + webkit_web_view_execute_editing_command ( + WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_CUT); } gboolean e_web_view_is_selection_active (EWebView *web_view) { + GDBusProxy *web_extension; + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view)); + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (web_extension) { + GVariant *result; + + result = g_dbus_proxy_call_sync ( + web_extension, + "DocumentHasSelection", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (web_view))), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + gboolean value = FALSE; + + g_variant_get (result, "(b)", &value); + g_variant_unref (result); + return value; + } + } + + return FALSE; } void @@ -2435,17 +2913,18 @@ e_web_view_paste_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - webkit_web_view_paste_clipboard (WEBKIT_WEB_VIEW (web_view)); + webkit_web_view_execute_editing_command ( + WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_PASTE); } gboolean e_web_view_scroll_forward (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - +/* FIXME WK2 webkit_web_view_move_cursor ( WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, 1); - +*/ return TRUE; /* XXX This means nothing. */ } @@ -2453,10 +2932,10 @@ gboolean e_web_view_scroll_backward (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - +/* FIXME WK2 webkit_web_view_move_cursor ( WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, -1); - +*/ return TRUE; /* XXX This means nothing. */ } @@ -2465,7 +2944,8 @@ e_web_view_select_all (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - webkit_web_view_select_all (WEBKIT_WEB_VIEW (web_view)); + webkit_web_view_execute_editing_command ( + WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_SELECT_ALL); } void @@ -2489,19 +2969,31 @@ e_web_view_zoom_100 (EWebView *web_view) void e_web_view_zoom_in (EWebView *web_view) { + gdouble zoom_level; + g_return_if_fail (E_IS_WEB_VIEW (web_view)); - if (webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)) < 4.9999) - webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (web_view)); + /* There is no webkit_web_view_zoom_in function in WK2, so emulate it */ + zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)); + /* zoom-step in WK1 was 0.1 */ + zoom_level += 0.1; + if (zoom_level < 4.9999) + webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level); } void e_web_view_zoom_out (EWebView *web_view) { + gdouble zoom_level; + g_return_if_fail (E_IS_WEB_VIEW (web_view)); - if (webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)) > 0.7999) - webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (web_view)); + /* There is no webkit_web_view_zoom_out function in WK2, so emulate it */ + zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)); + /* zoom-step in WK1 was 0.1 */ + zoom_level -= 0.1; + if (zoom_level > 0.7999) + webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level); } GtkUIManager * @@ -2609,132 +3101,98 @@ e_web_view_update_actions (EWebView *web_view) g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0); } -static gboolean -element_is_in_pre_tag (WebKitDOMNode *node) +static void +get_selection_content_html_cb (GDBusProxy *web_extension, + GAsyncResult *result, + GTask *task) { - WebKitDOMElement *element; - - if (!node) - return FALSE; + GVariant *result_variant; + gchar *html_content = NULL; - while (element = webkit_dom_node_get_parent_element (node), element) { - node = WEBKIT_DOM_NODE (element); + result_variant = g_dbus_proxy_call_finish (web_extension, result, NULL); + if (result_variant) + g_variant_get (result_variant, "(s)", &html_content); + g_variant_unref (result_variant); - if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (element)) { - return TRUE; - } else if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) { - break; - } - } - - return FALSE; + g_task_return_pointer (task, html_content, g_free); + g_object_unref (task); } -static gchar * -web_view_get_frame_selection_html (WebKitDOMElement *iframe) -{ - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMNodeList *frames; - gulong ii, length; - - document = webkit_dom_html_iframe_element_get_content_document ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) { - WebKitDOMRange *range; - WebKitDOMElement *element; - WebKitDOMDocumentFragment *fragment; - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - if (range != NULL) { - gchar *inner_html; - WebKitDOMNode *node; - - fragment = webkit_dom_range_clone_contents ( - range, NULL); - - element = webkit_dom_document_create_element ( - document, "DIV", NULL); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (element), - WEBKIT_DOM_NODE (fragment), NULL); - - inner_html = webkit_dom_html_element_get_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element)); - node = webkit_dom_range_get_start_container (range, NULL); - if (element_is_in_pre_tag (node)) { - gchar *tmp = inner_html; - inner_html = g_strconcat ("<pre>", tmp, "</pre>", NULL); - g_free (tmp); - } - - g_object_unref (range); - g_object_unref (dom_selection); - return inner_html; - } - } - - g_object_unref (dom_selection); - - frames = webkit_dom_document_get_elements_by_tag_name ( - document, "IFRAME"); - length = webkit_dom_node_list_get_length (frames); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - gchar *text; - - node = webkit_dom_node_list_item (frames, ii); - - text = web_view_get_frame_selection_html ( - WEBKIT_DOM_ELEMENT (node)); +void +e_web_view_get_selection_content_html (EWebView *web_view, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusProxy *web_extension; + GTask *task; - g_object_unref (node); - if (text != NULL) { - g_object_unref (frames); - return text; - } - } - g_object_unref (frames); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); - return NULL; + task = g_task_new (web_view, cancellable, callback, user_data); + + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (web_extension) { + g_dbus_proxy_call ( + web_extension, + "GetSelectionContentHTML", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (web_view))), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + (GAsyncReadyCallback) get_selection_content_html_cb, + g_object_ref (task)); + } else + g_task_return_pointer (task, NULL, NULL); } gchar * -e_web_view_get_selection_html (EWebView *web_view) +e_web_view_get_selection_content_html_finish (EWebView *web_view, + GAsyncResult *result, + GError **error) { - WebKitDOMDocument *document; - WebKitDOMNodeList *frames; - gulong ii, length; - g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + g_return_val_if_fail (g_task_is_valid (result, web_view), FALSE); - if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view))) - return NULL; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view)); - frames = webkit_dom_document_get_elements_by_tag_name (document, "IFRAME"); - length = webkit_dom_node_list_get_length (frames); - - for (ii = 0; ii < length; ii++) { - gchar *text; - WebKitDOMNode *node; + return g_task_propagate_pointer (G_TASK (result), error); +} - node = webkit_dom_node_list_item (frames, ii); +gchar * +e_web_view_get_selection_content_html_sync (EWebView *web_view, + GCancellable *cancellable, + GError **error) +{ + GDBusProxy *web_extension; - text = web_view_get_frame_selection_html ( - WEBKIT_DOM_ELEMENT (node)); + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - g_object_unref (node); - if (text != NULL) { - g_object_unref (frames); - return text; + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (web_extension) { + GVariant *result; + + result = g_dbus_proxy_call_sync ( + web_extension, + "GetSelectionContentHTML", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (web_view))), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + + if (result) { + gchar *html_content = NULL; + + g_variant_get (result, "(s)", &html_content); + g_variant_unref (result); + return html_content; } } - g_object_unref (frames); return NULL; } @@ -2757,60 +3215,61 @@ e_web_view_get_citation_color_for_level (gint level) } void -e_web_view_update_fonts (EWebView *web_view) +e_web_view_update_fonts_settings (GSettings *font_settings, + GSettings *aliasing_settings, + PangoFontDescription *ms_font, + PangoFontDescription *vw_font, + GtkWidget *view_widget) { - EWebViewClass *class; - GString *stylesheet; - gchar *base64; + gboolean clean_ms = FALSE, clean_vw = FALSE; gchar *aa = NULL; - WebKitWebSettings *settings; - PangoFontDescription *min_size, *ms, *vw; const gchar *styles[] = { "normal", "oblique", "italic" }; const gchar *smoothing = NULL; - GtkStyleContext *context; GdkColor *link = NULL; GdkColor *visited = NULL; + GString *stylesheet; + GtkStyleContext *context; + PangoFontDescription *min_size, *ms, *vw; + WebKitSettings *wk_settings; + WebKitUserContentManager *manager; + WebKitUserStyleSheet *style_sheet; - g_return_if_fail (E_IS_WEB_VIEW (web_view)); - - ms = NULL; - vw = NULL; - - class = E_WEB_VIEW_GET_CLASS (web_view); - if (class->set_fonts != NULL) - class->set_fonts (web_view, &ms, &vw); - - if (ms == NULL) { + if (!ms_font) { gchar *font; font = g_settings_get_string ( - web_view->priv->font_settings, + font_settings, "monospace-font-name"); ms = pango_font_description_from_string ( (font != NULL) ? font : "monospace 10"); + clean_ms = TRUE; + g_free (font); - } + } else + ms = ms_font; - if (vw == NULL) { + if (!vw_font) { gchar *font; font = g_settings_get_string ( - web_view->priv->font_settings, + font_settings, "font-name"); vw = pango_font_description_from_string ( (font != NULL) ? font : "serif 10"); + clean_vw = TRUE; + g_free (font); - } + } else + vw = vw_font; - if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw)) { + if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw)) min_size = ms; - } else { + else min_size = vw; - } stylesheet = g_string_new (""); g_string_append_printf ( @@ -2825,9 +3284,9 @@ e_web_view_update_fonts (EWebView *web_view) pango_font_description_get_weight (vw), styles[pango_font_description_get_style (vw)]); - if (web_view->priv->aliasing_settings != NULL) + if (aliasing_settings != NULL) aa = g_settings_get_string ( - web_view->priv->aliasing_settings, "antialiasing"); + aliasing_settings, "antialiasing"); if (g_strcmp0 (aa, "none") == 0) smoothing = "none"; @@ -2854,176 +3313,229 @@ e_web_view_update_fonts (EWebView *web_view) " font-weight: %d;\n" " font-style: %s;\n" " margin: 0px;\n" - "}", + "}\n", pango_font_description_get_family (ms), pango_font_description_get_size (ms) / PANGO_SCALE, pango_font_description_get_weight (ms), styles[pango_font_description_get_style (ms)]); - context = gtk_widget_get_style_context (GTK_WIDGET (web_view)); - gtk_style_context_get_style ( - context, - "link-color", &link, - "visited-link-color", &visited, - NULL); - - if (link == NULL) { - #if GTK_CHECK_VERSION(3,12,0) - GdkRGBA rgba; - GtkStateFlags state; - #endif - - link = g_slice_new0 (GdkColor); - link->blue = G_MAXINT16; + if (view_widget) { + context = gtk_widget_get_style_context (view_widget); + gtk_style_context_get_style ( + context, + "link-color", &link, + "visited-link-color", &visited, + NULL); - #if GTK_CHECK_VERSION(3,12,0) - rgba.alpha = 1; - rgba.red = 0; - rgba.green = 0; - rgba.blue = 1; + if (link == NULL) { + #if GTK_CHECK_VERSION(3,12,0) + GdkRGBA rgba; + GtkStateFlags state; + #endif - state = gtk_style_context_get_state (context); - state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK)); - state = state | GTK_STATE_FLAG_LINK; + link = g_slice_new0 (GdkColor); + link->blue = G_MAXINT16; - gtk_style_context_save (context); - gtk_style_context_set_state (context, state); - gtk_style_context_get_color (context, state, &rgba); - gtk_style_context_restore (context); + #if GTK_CHECK_VERSION(3,12,0) + rgba.alpha = 1; + rgba.red = 0; + rgba.green = 0; + rgba.blue = 1; - e_rgba_to_color (&rgba, link); - #endif - } + state = gtk_style_context_get_state (context); + state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK)); + state = state | GTK_STATE_FLAG_LINK; - if (visited == NULL) { - #if GTK_CHECK_VERSION(3,12,0) - GdkRGBA rgba; - GtkStateFlags state; - #endif + gtk_style_context_save (context); + gtk_style_context_set_state (context, state); + gtk_style_context_get_color (context, state, &rgba); + gtk_style_context_restore (context); - visited = g_slice_new0 (GdkColor); - visited->red = G_MAXINT16; - - #if GTK_CHECK_VERSION(3,12,0) - rgba.alpha = 1; - rgba.red = 1; - rgba.green = 0; - rgba.blue = 0; + e_rgba_to_color (&rgba, link); + #endif + } - state = gtk_style_context_get_state (context); - state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK)); - state = state | GTK_STATE_FLAG_VISITED; + if (visited == NULL) { + #if GTK_CHECK_VERSION(3,12,0) + GdkRGBA rgba; + GtkStateFlags state; + #endif - gtk_style_context_save (context); - gtk_style_context_set_state (context, state); - gtk_style_context_get_color (context, state, &rgba); - gtk_style_context_restore (context); + visited = g_slice_new0 (GdkColor); + visited->red = G_MAXINT16; - e_rgba_to_color (&rgba, visited); - #endif - } + #if GTK_CHECK_VERSION(3,12,0) + rgba.alpha = 1; + rgba.red = 1; + rgba.green = 0; + rgba.blue = 0; - g_string_append_printf ( - stylesheet, - "a {\n" - " color: #%06x;\n" - "}\n" - "a:visited {\n" - " color: #%06x;\n" - "}\n", - e_color_to_value (link), - e_color_to_value (visited)); + state = gtk_style_context_get_state (context); + state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK)); + state = state | GTK_STATE_FLAG_VISITED; - gdk_color_free (link); - gdk_color_free (visited); + gtk_style_context_save (context); + gtk_style_context_set_state (context, state); + gtk_style_context_get_color (context, state, &rgba); + gtk_style_context_restore (context); - g_string_append ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " padding: 0ch 1ch 0ch 1ch;\n" - " margin: 0ch;\n" - " border-width: 0px 2px 0px 2px;\n" - " border-style: none solid none solid;\n" - " border-radius: 2px;\n" - "}\n"); + e_rgba_to_color (&rgba, visited); + #endif + } - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (1)); + g_string_append_printf ( + stylesheet, + "a {\n" + " color: #%06x;\n" + "}\n" + "a:visited {\n" + " color: #%06x;\n" + "}\n", + e_color_to_value (link), + e_color_to_value (visited)); + + gdk_color_free (link); + gdk_color_free (visited); + + g_string_append ( + stylesheet, + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "{\n" + " padding: 0ch 1ch 0ch 1ch;\n" + " margin: 0ch;\n" + " border-width: 0px 2px 0px 2px;\n" + " border-style: none solid none solid;\n" + " border-radius: 2px;\n" + "}\n"); - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (2)); + g_string_append_printf ( + stylesheet, + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (1)); - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (3)); + g_string_append_printf ( + stylesheet, + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (2)); - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (4)); + g_string_append_printf ( + stylesheet, + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (3)); - g_string_append_printf ( - stylesheet, - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " - "{\n" - " border-color: %s;\n" - "}\n", - e_web_view_get_citation_color_for_level (5)); + g_string_append_printf ( + stylesheet, + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (4)); - base64 = g_base64_encode ((guchar *) stylesheet->str, stylesheet->len); - g_string_free (stylesheet, TRUE); + g_string_append_printf ( + stylesheet, + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (5)); + } - stylesheet = g_string_new ("data:text/css;charset=utf-8;base64,"); - g_string_append (stylesheet, base64); - g_free (base64); + wk_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view_widget)); - settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view)); g_object_set ( - G_OBJECT (settings), + wk_settings, "default-font-size", - pango_font_description_get_size (vw) / PANGO_SCALE, + e_util_normalize_font_size ( + view_widget, pango_font_description_get_size (vw) / PANGO_SCALE), "default-font-family", pango_font_description_get_family (vw), "monospace-font-family", pango_font_description_get_family (ms), "default-monospace-font-size", - pango_font_description_get_size (ms) / PANGO_SCALE, + e_util_normalize_font_size ( + view_widget, pango_font_description_get_size (ms) / PANGO_SCALE), "minimum-font-size", - pango_font_description_get_size (min_size) / PANGO_SCALE, - "user-stylesheet-uri", + e_util_normalize_font_size ( + view_widget, pango_font_description_get_size (min_size) / PANGO_SCALE), + NULL); + + manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (view_widget)); + webkit_user_content_manager_remove_all_style_sheets (manager); + + style_sheet = webkit_user_style_sheet_new ( stylesheet->str, + WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, + WEBKIT_USER_STYLE_LEVEL_USER, + NULL, NULL); + webkit_user_content_manager_add_style_sheet (manager, style_sheet); + + webkit_user_style_sheet_unref (style_sheet); + g_string_free (stylesheet, TRUE); + if (clean_ms) + pango_font_description_free (ms); + if (clean_vw) + pango_font_description_free (vw); +} + +WebKitSettings * +e_web_view_get_default_webkit_settings (void) +{ + return webkit_settings_new_with_settings ( + "auto-load-images", TRUE, + "default-charset", "utf-8", + "enable-html5-database", FALSE, + "enable-dns-prefetching", FALSE, + "enable-html5-local-storage", FALSE, + "enable-java", FALSE, + "enable-javascript", FALSE, + "enable-offline-web-application-cache", FALSE, + "enable-page-cache", FALSE, + "enable-plugins", FALSE, + "enable-smooth-scrolling", FALSE, + "media-playback-allows-inline", FALSE, + NULL); +} + +void +e_web_view_update_fonts (EWebView *web_view) +{ + EWebViewClass *class; + PangoFontDescription *ms = NULL, *vw = NULL; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + class = E_WEB_VIEW_GET_CLASS (web_view); + if (class->set_fonts != NULL) + class->set_fonts (web_view, &ms, &vw); + + e_web_view_update_fonts_settings ( + web_view->priv->font_settings, + web_view->priv->aliasing_settings, + ms, vw, GTK_WIDGET (web_view)); + pango_font_description_free (ms); pango_font_description_free (vw); } @@ -3387,24 +3899,25 @@ e_web_view_cursor_image_save (EWebView *web_view) /* Helper for e_web_view_request() */ static void -web_view_request_send_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) +web_view_request_process_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) { - GSimpleAsyncResult *simple; - AsyncContext *async_context; + AsyncContext *async_context = task_data; + gint64 stream_length = -1; + gchar *mime_type = NULL; GError *local_error = NULL; - simple = G_SIMPLE_ASYNC_RESULT (user_data); - async_context = g_simple_async_result_get_op_res_gpointer (simple); - - async_context->input_stream = soup_request_send_finish ( - SOUP_REQUEST (source_object), result, &local_error); - - if (local_error != NULL) - g_simple_async_result_take_error (simple, local_error); + if (!e_content_request_process_sync (async_context->content_request, + async_context->uri, source_object, &async_context->input_stream, + &stream_length, &mime_type, cancellable, &local_error)) { + g_task_return_error (task, local_error); + } else { + g_task_return_boolean (task, TRUE); + } - g_simple_async_result_complete (simple); + g_free (mime_type); } /** @@ -3415,9 +3928,7 @@ web_view_request_send_cb (GObject *source_object, * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * - * Asynchronously requests data at @uri by way of a #SoupRequest to WebKit's - * default #SoupSession, incorporating both e_web_view_redirect_uri() and the - * custom request handlers installed via e_web_view_install_request_handler(). + * Asynchronously requests data at @uri as displaed in the @web_view. * * When the operation is finished, @callback will be called. You can then * call e_web_view_request_finish() to get the result of the operation. @@ -3429,52 +3940,41 @@ e_web_view_request (EWebView *web_view, GAsyncReadyCallback callback, gpointer user_data) { - SoupSession *session; - SoupRequest *request; - gchar *real_uri; - GSimpleAsyncResult *simple; + EContentRequest *content_request = NULL; AsyncContext *async_context; - GError *local_error = NULL; + GSList *link; + GTask *task; g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_return_if_fail (uri != NULL); - session = webkit_get_default_session (); - - async_context = g_slice_new0 (AsyncContext); - - simple = g_simple_async_result_new ( - G_OBJECT (web_view), callback, - user_data, e_web_view_request); + for (link = web_view->priv->content_requests; link; link = g_slist_next (link)) { + EContentRequest *adept = link->data; - g_simple_async_result_set_check_cancellable (simple, cancellable); + if (!E_IS_CONTENT_REQUEST (adept) || + !e_content_request_can_process_uri (adept, uri)) + continue; - g_simple_async_result_set_op_res_gpointer ( - simple, async_context, (GDestroyNotify) async_context_free); - - real_uri = e_web_view_redirect_uri (web_view, uri); - request = soup_session_request (session, real_uri, &local_error); - g_free (real_uri); - - /* Sanity check. */ - g_return_if_fail ( - ((request != NULL) && (local_error == NULL)) || - ((request == NULL) && (local_error != NULL))); + content_request = adept; + break; + } - if (request != NULL) { - soup_request_send_async ( - request, cancellable, - web_view_request_send_cb, - g_object_ref (simple)); + async_context = g_slice_new0 (AsyncContext); + async_context->uri = g_strdup (uri); - g_object_unref (request); + task = g_task_new (web_view, cancellable, callback, user_data); + g_task_set_task_data (task, async_context, async_context_free); + g_task_set_check_cancellable (task, TRUE); + if (content_request) { + async_context->content_request = g_object_ref (content_request); + g_task_run_in_thread (task, web_view_request_process_thread); } else { - g_simple_async_result_take_error (simple, local_error); - g_simple_async_result_complete_in_idle (simple); + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Cannot get URI '%s', do not know how to download it."), uri); } - g_object_unref (simple); + g_object_unref (task); } /** @@ -3496,204 +3996,435 @@ e_web_view_request_finish (EWebView *web_view, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; AsyncContext *async_context; - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (web_view), e_web_view_request), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_context = g_simple_async_result_get_op_res_gpointer (simple); + g_return_val_if_fail (g_task_is_valid (result, web_view), NULL); - if (g_simple_async_result_propagate_error (simple, error)) + if (!g_task_propagate_boolean (G_TASK (result), error)) return NULL; + async_context = g_task_get_task_data (G_TASK (result)); + g_return_val_if_fail (async_context->input_stream != NULL, NULL); return g_object_ref (async_context->input_stream); } +/** + * e_web_view_create_and_add_css_style_sheet: + * @web_view: an #EWebView + * @style_sheet_id: CSS style sheet's id + * + * Creates new CSS style sheet with given @style_sheel_id and inserts + * it into given @web_view document. + **/ void -e_web_view_install_request_handler (EWebView *web_view, - GType handler_type) +e_web_view_create_and_add_css_style_sheet (EWebView *web_view, + const gchar *style_sheet_id) { - SoupSession *session; + GDBusProxy *web_extension; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (style_sheet_id && *style_sheet_id); - session = webkit_get_default_session (); - soup_session_add_feature_by_type (session, handler_type); + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (web_extension) { + g_dbus_proxy_call ( + web_extension, + "CreateAndAddCSSStyleSheet", + g_variant_new ( + "(ts)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (web_view)), + style_sheet_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } } +/** + * e_web_view_add_css_rule_into_style_sheet: + * @web_view: an #EWebView + * @style_sheet_id: CSS style sheet's id + * @selector: CSS selector + * @style: style for given selector + * + * Insert new CSS rule (defined with @selector and @style) into CSS style sheet + * with given @style_sheet_id. If style sheet doesn't exist, it's created. + * + * The rule is inserted to every DOM document that is in page. That means also + * into DOM documents inside iframe elements. + **/ void -e_web_view_create_and_add_css_style_sheet (WebKitDOMDocument *document, - const gchar *style_sheet_id) +e_web_view_add_css_rule_into_style_sheet (EWebView *web_view, + const gchar *style_sheet_id, + const gchar *selector, + const gchar *style) { - WebKitDOMElement *style_element; + GDBusProxy *web_extension; - style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (style_sheet_id && *style_sheet_id); + g_return_if_fail (selector && *selector); + g_return_if_fail (style && *style); - if (!style_element) { - WebKitDOMText *dom_text; - WebKitDOMHTMLHeadElement *head; + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (web_extension) { + g_dbus_proxy_call ( + web_extension, + "AddCSSRuleIntoStyleSheet", + g_variant_new ( + "(tsss)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (web_view)), + style_sheet_id, + selector, + style), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } +} - dom_text = webkit_dom_document_create_text_node (document, ""); +/** + * e_web_view_get_document_uri_from_point: + * @web_view: an #EWebView + * @x: x-coordinate + * @y: y-coordinate + * + * Returns: A document URI which is under the @x, @y coordinates or %NULL, + * if there is none. Free the returned pointer with g_free() when done with it. + * + * Since: 3.22 + **/ +gchar * +e_web_view_get_document_uri_from_point (EWebView *web_view, + gint32 x, + gint32 y) +{ + GDBusProxy *web_extension; + GVariant *result; + GError *local_error = NULL; - /* Create new <style> element */ - style_element = webkit_dom_document_create_element (document, "style", NULL); - webkit_dom_element_set_id ( - WEBKIT_DOM_ELEMENT (style_element), - style_sheet_id); - webkit_dom_html_style_element_set_media ( - WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element), - "screen"); - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (style_element), - /* WebKit hack - we have to insert empty TextNode into style element */ - WEBKIT_DOM_NODE (dom_text), - NULL); + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - head = webkit_dom_document_get_head (document); + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (!web_extension) + return NULL; - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (head), - WEBKIT_DOM_NODE (style_element), - NULL); + result = g_dbus_proxy_call_sync ( + web_extension, + "GetDocumentURIFromPoint", + g_variant_new ( + "(tii)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)), + x, + y), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &local_error); + + if (local_error) + g_warning ("%s: Failed with error: %s", G_STRFUNC, local_error->message); + + g_clear_error (&local_error); + + if (result) { + gchar *uri = NULL; + + g_variant_get (result, "(s)", &uri); + g_variant_unref (result); + + if (g_strcmp0 (uri, "") == 0) { + g_free (uri); + uri = NULL; + } - g_object_unref (head); - g_object_unref (dom_text); - g_object_unref (style_element); + return uri; } + + return NULL; } static void -add_css_rule_into_style_sheet (WebKitDOMDocument *document, - const gchar *style_sheet_id, - const gchar *selector, - const gchar *style) +e_web_view_set_document_iframe_src_done_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - WebKitDOMElement *style_element; - WebKitDOMStyleSheet *sheet; - WebKitDOMCSSRuleList *rules_list; - gint length, ii, selector_length; - gboolean removed = FALSE; + GVariant *variant; + GError *local_error = NULL; - g_return_if_fail (selector != NULL); + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), result, &local_error); + if (variant) + g_variant_unref (variant); - selector_length = strlen (selector); - style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id); + if (local_error) + g_warning ("%s: Failed with error: %s", G_STRFUNC, local_error->message); - if (!style_element) { - e_web_view_create_and_add_css_style_sheet (document, style_sheet_id); - style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id); - } + g_clear_error (&local_error); +} - /* Get sheet that is associated with style element */ - sheet = webkit_dom_html_style_element_get_sheet (WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element)); +/** + * e_web_view_set_document_iframe_src: + * @web_view: an #EWebView + * @document_uri: a document URI for whose IFrame change the source + * @new_iframe_src: the source to change the IFrame to + * + * Change IFrame source for the given @document_uri IFrame + * to the @new_iframe_src. + * + * Since: 3.22 + **/ +void +e_web_view_set_document_iframe_src (EWebView *web_view, + const gchar *document_uri, + const gchar *new_iframe_src) +{ + GDBusProxy *web_extension; - rules_list = webkit_dom_css_style_sheet_get_css_rules (WEBKIT_DOM_CSS_STYLE_SHEET (sheet)); - length = webkit_dom_css_rule_list_get_length (rules_list); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); - /* Check if rule exists */ - for (ii = 0; ii < length && !removed; ii++) { - WebKitDOMCSSRule *rule; - gchar *rule_text = NULL; + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (!web_extension) + return; - rule = webkit_dom_css_rule_list_item (rules_list, ii); + /* Cannot call this synchronously, blocking the local main loop, because the reload + can on the WebProcess side can be asking for a redirection policy, waiting + for a response which may be waiting in the blocked main loop. */ + g_dbus_proxy_call ( + web_extension, + "SetDocumentIFrameSrc", + g_variant_new ( + "(tss)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)), + document_uri, + new_iframe_src), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + e_web_view_set_document_iframe_src_done_cb, NULL); +} - g_return_if_fail (WEBKIT_DOM_IS_CSS_RULE (rule)); +/** + * EWebViewElementClickedFunc: + * @web_view: an #EWebView + * @element_class: an element class, as set on the element which had been clicked + * @element_value: a 'value' attribute content of the clicked element + * @element_position: a #GtkAllocation with the position of the clicked element + * @user_data: user data as provided in the e_web_view_register_element_clicked() call + * + * The callback is called whenever an element of class @element_class is clicked. + * The @element_value is a content of the 'value' attribute of the clicked element. + * The @element_position is the place of the element within the web page, already + * accounting scrollbar positions. + * + * See: e_web_view_register_element_clicked, e_web_view_unregister_element_clicked + * + * Since: 3.22 + **/ - rule_text = webkit_dom_css_rule_get_css_text (rule); +/** + * e_web_view_register_element_clicked: + * @web_view: an #EWebView + * @element_class: an element class on which to listen for clicking + * @callback: an #EWebViewElementClickedFunc to call, when the element is clicked + * @user_data: user data to pass to @callback + * + * Registers a @callback to be called when any element of the class @element_class + * is clicked. If the element contains a 'value' attribute, then it is passed to + * the @callback too. These callback are valid until a new content of the @web_view + * is loaded, after which all the registered callbacks are forgotten. + * + * Since: 3.22 + **/ +void +e_web_view_register_element_clicked (EWebView *web_view, + const gchar *element_class, + EWebViewElementClickedFunc callback, + gpointer user_data) +{ + ElementClickedData *ecd; + GPtrArray *cbs; + guint ii; - /* Find the start of the style => end of the selector */ - if (rule_text && selector && g_str_has_prefix (rule_text, selector) && - rule_text[selector_length] == ' ' && rule_text[selector_length + 1] == '{') { - /* If exists remove it */ - webkit_dom_css_style_sheet_remove_rule ( - WEBKIT_DOM_CSS_STYLE_SHEET (sheet), - ii, NULL); - length--; - removed = TRUE; + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (element_class != NULL); + g_return_if_fail (callback != NULL); + + cbs = g_hash_table_lookup (web_view->priv->element_clicked_cbs, element_class); + if (cbs) { + for (ii = 0; ii < cbs->len; ii++) { + ecd = g_ptr_array_index (cbs, ii); + + if (ecd && ecd->callback == callback && ecd->user_data == user_data) { + /* Callback is already registered, but re-register it, in case the page + was changed dynamically and new elements with the given call are added. */ + web_view_register_element_clicked_hfunc ((gpointer) element_class, cbs, web_view); + return; + } } + } + + ecd = g_new0 (ElementClickedData, 1); + ecd->callback = callback; + ecd->user_data = user_data; + + if (!cbs) { + cbs = g_ptr_array_new_full (1, g_free); + g_ptr_array_add (cbs, ecd); - g_free (rule_text); - g_object_unref (rule); + g_hash_table_insert (web_view->priv->element_clicked_cbs, g_strdup (element_class), cbs); + } else { + g_ptr_array_add (cbs, ecd); } - g_object_unref (rules_list); + /* Dynamically changing page can call this multiple times; re-register all classes */ + g_hash_table_foreach (web_view->priv->element_clicked_cbs, web_view_register_element_clicked_hfunc, web_view); +} + +/** + * e_web_view_unregister_element_clicked: + * @web_view: an #EWebView + * @element_class: an element class on which to listen for clicking + * @callback: an #EWebViewElementClickedFunc to call, when the element is clicked + * @user_data: user data to pass to @callback + * + * Unregisters the @callback for the @element_class with the given @user_data, which + * should be previously registered with e_web_view_register_element_clicked(). This + * unregister is usually not needed, because the @web_view unregisters all callbacks + * when a new content is loaded. + * + * Since: 3.22 + **/ +void +e_web_view_unregister_element_clicked (EWebView *web_view, + const gchar *element_class, + EWebViewElementClickedFunc callback, + gpointer user_data) +{ + ElementClickedData *ecd; + GPtrArray *cbs; + guint ii; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (element_class != NULL); + g_return_if_fail (callback != NULL); + + cbs = g_hash_table_lookup (web_view->priv->element_clicked_cbs, element_class); + if (!cbs) + return; - /* Insert the rule at the end, so it will override previously inserted */ - webkit_dom_css_style_sheet_add_rule ( - WEBKIT_DOM_CSS_STYLE_SHEET (sheet), selector, style, length, NULL); + for (ii = 0; ii < cbs->len; ii++) { + ecd = g_ptr_array_index (cbs, ii); - g_object_unref (sheet); - g_object_unref (style_element); + if (ecd && ecd->callback == callback && ecd->user_data == user_data) { + g_ptr_array_remove (cbs, ecd); + if (!cbs->len) + g_hash_table_remove (web_view->priv->element_clicked_cbs, element_class); + break; + } + } } -static void -add_css_rule_into_style_sheet_recursive (WebKitDOMDocument *document, - const gchar *style_sheet_id, - const gchar *selector, - const gchar *style) +void +e_web_view_set_element_hidden (EWebView *web_view, + const gchar *element_id, + gboolean hidden) { - WebKitDOMNodeList *frames; - gint ii, length; + GDBusProxy *web_extension; - /* Add rule to document */ - add_css_rule_into_style_sheet ( - document, - style_sheet_id, - selector, - style); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (element_id && *element_id); - frames = webkit_dom_document_query_selector_all (document, "iframe", NULL); - length = webkit_dom_node_list_get_length (frames); + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (!web_extension) + return; - /* Add rules to every sub document */ - for (ii = 0; ii < length; ii++) { - WebKitDOMDocument *iframe_document; - WebKitDOMNode *node; + g_dbus_proxy_call ( + web_extension, + "SetElementHidden", + g_variant_new ( + "(tsb)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)), + element_id, + hidden), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} - node = webkit_dom_node_list_item (frames, ii); - iframe_document = webkit_dom_html_iframe_element_get_content_document ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (node)); +void +e_web_view_set_element_style_property (EWebView *web_view, + const gchar *element_id, + const gchar *property_name, + const gchar *value, + const gchar *priority) +{ + GDBusProxy *web_extension; - add_css_rule_into_style_sheet_recursive ( - iframe_document, - style_sheet_id, - selector, - style); - g_object_unref (node); - } - g_object_unref (frames); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (element_id && *element_id); + g_return_if_fail (property_name && *property_name); + + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (!web_extension) + return; + + g_dbus_proxy_call ( + web_extension, + "SetElementStyleProperty", + g_variant_new ( + "(tssss)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)), + element_id, + property_name, + value ? value : "", + priority ? priority : ""), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } -/** - * e_web_view_add_css_rule_into_style_sheet: - * @web_view: an #EWebView - * @style_sheet_id: CSS style sheet's id - * @selector: CSS selector - * @style: style for given selector - * - * Insert new CSS rule (defined with @selector and @style) into CSS style sheet - * with given @style_sheet_id. If style sheet doesn't exist, it's created. - * - * The rule is inserted to every DOM document that is in page. That means also - * into DOM documents inside iframe elements. - **/ void -e_web_view_add_css_rule_into_style_sheet (EWebView *view, - const gchar *style_sheet_id, - const gchar *selector, - const gchar *style) +e_web_view_set_element_attribute (EWebView *web_view, + const gchar *element_id, + const gchar *namespace_uri, + const gchar *qualified_name, + const gchar *value) { - g_return_if_fail (E_IS_WEB_VIEW (view)); - g_return_if_fail (style_sheet_id && *style_sheet_id); - g_return_if_fail (selector && *selector); - g_return_if_fail (style && *style); + GDBusProxy *web_extension; - add_css_rule_into_style_sheet_recursive ( - webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)), - style_sheet_id, - selector, - style); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (element_id && *element_id); + g_return_if_fail (qualified_name && *qualified_name); + + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (!web_extension) + return; + + g_dbus_proxy_call ( + web_extension, + "SetElementAttribute", + g_variant_new ( + "(tssss)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)), + element_id, + namespace_uri ? namespace_uri : "", + qualified_name, + value ? value : ""), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } diff --git a/e-util/e-web-view.h b/e-util/e-web-view.h index 545c3bb..62203d2 100644 --- a/e-util/e-web-view.h +++ b/e-util/e-web-view.h @@ -28,8 +28,9 @@ #ifndef E_WEB_VIEW_H #define E_WEB_VIEW_H -#include <webkit/webkit.h> +#include <webkit2/webkit2.h> #include <e-util/e-activity.h> +#include <e-util/e-content-request.h> /* Standard GObject macros */ #define E_TYPE_WEB_VIEW \ @@ -56,6 +57,21 @@ typedef struct _EWebView EWebView; typedef struct _EWebViewClass EWebViewClass; typedef struct _EWebViewPrivate EWebViewPrivate; +typedef enum { + CID_URI_SCHEME, + FILE_URI_SCHEME, + MAIL_URI_SCHEME, + EVO_HTTP_URI_SCHEME, + EVO_HTTPS_URI_SCHEME, + GTK_STOCK_URI_SCHEME +} EURIScheme; + +typedef void (*EWebViewElementClickedFunc) (EWebView *web_view, + const gchar *element_class, + const gchar *element_value, + const GtkAllocation *element_position, + gpointer user_data); + struct _EWebView { WebKitWebView parent; EWebViewPrivate *priv; @@ -78,8 +94,6 @@ struct _EWebViewClass { const gchar *load_string); void (*load_uri) (EWebView *web_view, const gchar *load_uri); - gchar * (*redirect_uri) (EWebView *web_view, - const gchar *uri); gchar * (*suggest_filename) (EWebView *web_view, const gchar *uri); void (*set_fonts) (EWebView *web_view, @@ -97,21 +111,48 @@ struct _EWebViewClass { void (*update_actions) (EWebView *web_view); gboolean (*process_mailto) (EWebView *web_view, const gchar *mailto_uri); + void (*uri_requested) (EWebView *web_view, + const gchar *uri, + gchar **redirect_to_uri); }; GType e_web_view_get_type (void) G_GNUC_CONST; GtkWidget * e_web_view_new (void); +WebKitSettings * + e_web_view_get_default_webkit_settings + (void); +void e_web_view_register_content_request_for_scheme + (EWebView *web_view, + const gchar *scheme, + EContentRequest *content_request); +void e_web_view_update_fonts_settings + (GSettings *font_settings, + GSettings *aliasing_settings, + PangoFontDescription *ms_font, + PangoFontDescription *vw_font, + GtkWidget *view_widget); void e_web_view_clear (EWebView *web_view); void e_web_view_load_string (EWebView *web_view, const gchar *string); void e_web_view_load_uri (EWebView *web_view, const gchar *uri); -gchar * e_web_view_redirect_uri (EWebView *web_view, - const gchar *uri); gchar * e_web_view_suggest_filename (EWebView *web_view, const gchar *uri); void e_web_view_reload (EWebView *web_view); -gchar * e_web_view_get_html (EWebView *web_view); +void e_web_view_get_content_html (EWebView *web_view, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gchar * e_web_view_get_content_html_finish + (EWebView *web_view, + GAsyncResult *result, + GError **error); +gchar * e_web_view_get_content_html_sync + (EWebView *web_view, + GCancellable *cancellable, + GError **error); +GDBusProxy * e_web_view_get_web_extension_proxy + (EWebView *web_view); gboolean e_web_view_get_caret_mode (EWebView *web_view); void e_web_view_set_caret_mode (EWebView *web_view, gboolean caret_mode); @@ -180,7 +221,19 @@ void e_web_view_status_message (EWebView *web_view, const gchar *status_message); void e_web_view_stop_loading (EWebView *web_view); void e_web_view_update_actions (EWebView *web_view); -gchar * e_web_view_get_selection_html (EWebView *web_view); +void e_web_view_get_selection_content_html + (EWebView *web_view, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gchar * e_web_view_get_selection_content_html_finish + (EWebView *web_view, + GAsyncResult *result, + GError **error); +gchar * e_web_view_get_selection_content_html_sync + (EWebView *web_view, + GCancellable *cancellable, + GError **error); void e_web_view_update_fonts (EWebView *web_view); void e_web_view_cursor_image_copy (EWebView *web_view); void e_web_view_cursor_image_save (EWebView *web_view); @@ -196,7 +249,7 @@ void e_web_view_install_request_handler (EWebView *web_view, GType handler_type); void e_web_view_create_and_add_css_style_sheet - (WebKitDOMDocument* document, + (EWebView *web_view, const gchar *style_sheet_id); void e_web_view_add_css_rule_into_style_sheet (EWebView *web_view, @@ -205,6 +258,39 @@ void e_web_view_add_css_rule_into_style_sheet const gchar *style); const gchar * e_web_view_get_citation_color_for_level (gint level); +gchar * e_web_view_get_document_uri_from_point + (EWebView *web_view, + gint32 x, + gint32 y); +void e_web_view_set_document_iframe_src + (EWebView *web_view, + const gchar *document_uri, + const gchar *new_iframe_src); +void e_web_view_register_element_clicked + (EWebView *web_view, + const gchar *element_class, + EWebViewElementClickedFunc callback, + gpointer user_data); +void e_web_view_unregister_element_clicked + (EWebView *web_view, + const gchar *element_class, + EWebViewElementClickedFunc callback, + gpointer user_data); +void e_web_view_set_element_hidden (EWebView *web_view, + const gchar *element_id, + gboolean hidden); +void e_web_view_set_element_style_property + (EWebView *web_view, + const gchar *element_id, + const gchar *property_name, + const gchar *value, + const gchar *priority); +void e_web_view_set_element_attribute + (EWebView *web_view, + const gchar *element_id, + const gchar *namespace_uri, + const gchar *qualified_name, + const gchar *value); G_END_DECLS #endif /* E_WEB_VIEW_H */ diff --git a/e-util/test-html-editor-units-utils.c b/e-util/test-html-editor-units-utils.c new file mode 100644 index 0000000..911cc98 --- /dev/null +++ b/e-util/test-html-editor-units-utils.c @@ -0,0 +1,1045 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> + +#include "e-util/e-util.h" + +#include "test-html-editor-units-utils.h" + +static guint event_processing_delay_ms = 5; + +void +test_utils_set_event_processing_delay_ms (guint value) +{ + event_processing_delay_ms = value; +} + +guint +test_utils_get_event_processing_delay_ms (void) +{ + return event_processing_delay_ms; +} + +typedef struct _UndoContent { + gchar *html; + gchar *plain; +} UndoContent; + +static UndoContent * +undo_content_new (TestFixture *fixture) +{ + EContentEditor *cnt_editor; + UndoContent *uc; + + g_return_val_if_fail (fixture != NULL, NULL); + g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), NULL); + + cnt_editor = e_html_editor_get_content_editor (fixture->editor); + + uc = g_new0 (UndoContent, 1); + uc->html = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_HTML, NULL, NULL); + uc->plain = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_PLAIN, NULL, NULL); + + g_warn_if_fail (uc->html != NULL); + g_warn_if_fail (uc->plain != NULL); + + return uc; +} + +static void +undo_content_free (gpointer ptr) +{ + UndoContent *uc = ptr; + + if (uc) { + g_free (uc->html); + g_free (uc->plain); + g_free (uc); + } +} + +static gboolean +undo_content_test (TestFixture *fixture, + const UndoContent *uc, + gint cmd_index) +{ + EContentEditor *cnt_editor; + gchar *text; + + g_return_val_if_fail (fixture != NULL, FALSE); + g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE); + g_return_val_if_fail (uc != NULL, FALSE); + + cnt_editor = e_html_editor_get_content_editor (fixture->editor); + + text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_HTML, NULL, NULL); + g_return_val_if_fail (text != NULL, FALSE); + + if (!test_utils_html_equal (fixture, text, uc->html)) { + g_warning ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not match at command %d", G_STRFUNC, text, uc->html, cmd_index); + g_free (text); + return FALSE; + } + + g_free (text); + + text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_PLAIN, NULL, NULL); + g_return_val_if_fail (text != NULL, FALSE); + + if (!test_utils_html_equal (fixture, text, uc->plain)) { + g_warning ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not match at command %d", G_STRFUNC, text, uc->plain, cmd_index); + g_free (text); + return FALSE; + } + + g_free (text); + + return TRUE; +} + +static gboolean +test_utils_web_process_crashed_cb (WebKitWebView *web_view, + gpointer user_data) +{ + g_warning ("%s:", G_STRFUNC); + + return FALSE; +} + +typedef struct _CreateData { + gpointer async_data; + TestFixture *fixture; +} CreateData; + +static void +test_utils_html_editor_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateData *create_data = user_data; + TestFixture *fixture; + EContentEditor *cnt_editor; + GtkWidget *html_editor; + GError *error = NULL; + + g_return_if_fail (create_data != NULL); + + fixture = create_data->fixture; + + html_editor = e_html_editor_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create editor: %s", G_STRFUNC, error->message); + g_clear_error (&error); + return; + } + + fixture->editor = E_HTML_EDITOR (html_editor); + + g_object_set (G_OBJECT (fixture->editor), + "halign", GTK_ALIGN_FILL, + "hexpand", TRUE, + "valign", GTK_ALIGN_FILL, + "vexpand", TRUE, + NULL); + gtk_widget_show (GTK_WIDGET (fixture->editor)); + gtk_container_add (GTK_CONTAINER (fixture->window), GTK_WIDGET (fixture->editor)); + + /* Make sure this is off */ + test_utils_fixture_change_setting_boolean (fixture, + "org.gnome.evolution.mail", "prompt-on-composer-mode-switch", FALSE); + + cnt_editor = e_html_editor_get_content_editor (fixture->editor); + g_object_set (G_OBJECT (cnt_editor), + "halign", GTK_ALIGN_FILL, + "hexpand", TRUE, + "valign", GTK_ALIGN_FILL, + "vexpand", TRUE, + "height-request", 150, + NULL); + + g_signal_connect (cnt_editor, "web-process-crashed", + G_CALLBACK (test_utils_web_process_crashed_cb), NULL); + + gtk_window_set_focus (GTK_WINDOW (fixture->window), GTK_WIDGET (cnt_editor)); + gtk_widget_show (fixture->window); + + test_utils_async_call_finish (create_data->async_data); +} + +void +test_utils_fixture_set_up (TestFixture *fixture, + gconstpointer user_data) +{ + CreateData create_data; + + fixture->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + fixture->undo_stack = NULL; + fixture->key_state = 0; + + create_data.async_data = test_utils_async_call_prepare (); + create_data.fixture = fixture; + + e_html_editor_new (test_utils_html_editor_created_cb, &create_data); + + test_utils_async_call_wait (create_data.async_data, 5); + + g_warn_if_fail (fixture->editor != NULL); + g_warn_if_fail (E_IS_HTML_EDITOR (fixture->editor)); +} + +static void +free_old_settings (gpointer ptr) +{ + TestSettings *data = ptr; + + if (data) { + GSettings *settings; + + settings = e_util_ref_settings (data->schema); + g_settings_set_value (settings, data->key, data->old_value); + g_clear_object (&settings); + + g_variant_unref (data->old_value); + g_free (data->schema); + g_free (data->key); + g_free (data); + } +} + +void +test_utils_fixture_tear_down (TestFixture *fixture, + gconstpointer user_data) +{ + gtk_widget_destroy (GTK_WIDGET (fixture->window)); + fixture->editor = NULL; + + g_slist_free_full (fixture->settings, free_old_settings); + fixture->settings = NULL; + + g_slist_free_full (fixture->undo_stack, undo_content_free); + fixture->undo_stack = NULL; +} + +void +test_utils_fixture_change_setting (TestFixture *fixture, + const gchar *schema, + const gchar *key, + GVariant *value) +{ + TestSettings *data; + GSettings *settings; + + g_return_if_fail (fixture != NULL); + g_return_if_fail (schema != NULL); + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + + g_variant_ref_sink (value); + + settings = e_util_ref_settings (schema); + + data = g_new0 (TestSettings, 1); + data->schema = g_strdup (schema); + data->key = g_strdup (key); + data->old_value = g_variant_ref_sink (g_settings_get_value (settings, key)); + + /* Use prepend, thus the restore comes in the opposite order, thus a change + of the same key is not a problem. */ + fixture->settings = g_slist_prepend (fixture->settings, data); + + g_settings_set_value (settings, key, value); + + g_clear_object (&settings); + g_variant_unref (value); +} + +void +test_utils_fixture_change_setting_boolean (TestFixture *fixture, + const gchar *schema, + const gchar *key, + gboolean value) +{ + test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_boolean (value)); +} + +void +test_utils_fixture_change_setting_int32 (TestFixture *fixture, + const gchar *schema, + const gchar *key, + gint value) +{ + test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_int32 (value)); +} + +void +test_utils_fixture_change_setting_string (TestFixture *fixture, + const gchar *schema, + const gchar *key, + const gchar *value) +{ + test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_string (value)); +} + +static void +test_utils_flush_main_context (void) +{ + GMainContext *main_context; + + main_context = g_main_context_default (); + + while (g_main_context_pending (main_context)) { + g_main_context_iteration (main_context, FALSE); + } +} + +gpointer +test_utils_async_call_prepare (void) +{ + return g_main_loop_new (NULL, FALSE); +} + +typedef struct _AsynCallData { + GMainLoop *loop; + gboolean timeout_reached; +} AsyncCallData; + +static gboolean +test_utils_async_call_timeout_reached_cb (gpointer user_data) +{ + AsyncCallData *async_call_data = user_data; + + g_return_val_if_fail (async_call_data != NULL, FALSE); + g_return_val_if_fail (async_call_data->loop != NULL, FALSE); + g_return_val_if_fail (!async_call_data->timeout_reached, FALSE); + + if (!g_source_is_destroyed (g_main_current_source ())) { + async_call_data->timeout_reached = TRUE; + g_main_loop_quit (async_call_data->loop); + } + + return FALSE; +} + +gboolean +test_utils_async_call_wait (gpointer async_data, + guint timeout_seconds) +{ + GMainLoop *loop = async_data; + AsyncCallData async_call_data; + GSource *source = NULL; + + g_return_val_if_fail (loop != NULL, FALSE); + + async_call_data.loop = loop; + async_call_data.timeout_reached = FALSE; + + /* 0 is to wait forever */ + if (timeout_seconds > 0) { + source = g_timeout_source_new_seconds (timeout_seconds); + g_source_set_callback (source, test_utils_async_call_timeout_reached_cb, &async_call_data, NULL); + g_source_attach (source, NULL); + } + + g_main_loop_run (loop); + + if (source) { + g_source_destroy (source); + g_source_unref (source); + } + + test_utils_flush_main_context (); + + g_main_loop_unref (loop); + + return !async_call_data.timeout_reached; +} + +gboolean +test_utils_async_call_finish (gpointer async_data) +{ + GMainLoop *loop = async_data; + + g_return_val_if_fail (loop != NULL, FALSE); + + g_main_loop_quit (loop); + + return FALSE; +} + +gboolean +test_utils_wait_milliseconds (guint milliseconds) +{ + gpointer async_data; + + async_data = test_utils_async_call_prepare (); + g_timeout_add (milliseconds, test_utils_async_call_finish, async_data); + + return test_utils_async_call_wait (async_data, milliseconds / 1000 + 1); +} + +static void +test_utils_send_key_event (GtkWidget *widget, + GdkEventType type, + guint keyval, + guint state) +{ + GdkKeymap *keymap; + GdkKeymapKey *keys = NULL; + gint n_keys; + GdkEvent *event; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + event = gdk_event_new (type); + event->key.is_modifier = + keyval == GDK_KEY_Shift_L || + keyval == GDK_KEY_Shift_R || + keyval == GDK_KEY_Control_L || + keyval == GDK_KEY_Control_R || + keyval == GDK_KEY_Alt_L || + keyval == GDK_KEY_Alt_R; + event->key.keyval = keyval; + event->key.state = state; + event->key.window = g_object_ref (gtk_widget_get_window (widget)); + event->key.send_event = TRUE; + event->key.length = 0; + event->key.string = NULL; + event->key.hardware_keycode = 0; + event->key.group = 0; + event->key.time = GDK_CURRENT_TIME; + + gdk_event_set_device (event, gdk_seat_get_keyboard (gdk_display_get_default_seat (gtk_widget_get_display (widget)))); + + keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget)); + if (gdk_keymap_get_entries_for_keyval (keymap, keyval, &keys, &n_keys)) { + if (n_keys > 0) { + event->key.hardware_keycode = keys[0].keycode; + event->key.group = keys[0].group; + } + + g_free (keys); + } + + gtk_main_do_event (event); + + test_utils_wait_milliseconds (event_processing_delay_ms); + + gdk_event_free (event); +} + +gboolean +test_utils_type_text (TestFixture *fixture, + const gchar *text) +{ + GtkWidget *widget; + + g_return_val_if_fail (fixture != NULL, FALSE); + g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE); + + widget = GTK_WIDGET (e_html_editor_get_content_editor (fixture->editor)); + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + g_return_val_if_fail (text != NULL, FALSE); + g_return_val_if_fail (g_utf8_validate (text, -1, NULL), FALSE); + + while (*text) { + guint keyval; + gunichar unichar; + + unichar = g_utf8_get_char (text); + text = g_utf8_next_char (text); + + switch (unichar) { + case '\n': + keyval = GDK_KEY_Return; + break; + case '\t': + keyval = GDK_KEY_Tab; + break; + case '\b': + keyval = GDK_KEY_BackSpace; + break; + default: + keyval = gdk_unicode_to_keyval (unichar); + break; + } + + test_utils_send_key_event (widget, GDK_KEY_PRESS, keyval, fixture->key_state); + test_utils_send_key_event (widget, GDK_KEY_RELEASE, keyval, fixture->key_state); + } + + test_utils_wait_milliseconds (event_processing_delay_ms); + + return TRUE; +} + +static void +sync_wrapper_result_callback (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GAsyncResult **out_async_result = user_data; + + g_return_if_fail (out_async_result != NULL); + g_return_if_fail (*out_async_result == NULL); + + *out_async_result = g_object_ref (result); +} + +/* Wraps GDBusProxy synchronous call into an asynchronous without blocking + the main context, thus there is no freeze when this is called in the UI + process and the WebProcess also does its own IPC call. */ +static GVariant * +g_dbus_proxy_call_sync_wrapper (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error) +{ + GAsyncResult *async_result = NULL; + GVariant *var_result; + + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + g_return_val_if_fail (method_name != NULL, NULL); + + g_dbus_proxy_call ( + proxy, method_name, parameters, flags, timeout_msec, cancellable, + sync_wrapper_result_callback, &async_result); + + while (!async_result) { + g_main_context_iteration (NULL, TRUE); + } + + var_result = g_dbus_proxy_call_finish (proxy, async_result, error); + + g_clear_object (&async_result); + + return var_result; +} + +gboolean +test_utils_html_equal (TestFixture *fixture, + const gchar *html1, + const gchar *html2) +{ + EContentEditor *cnt_editor; + GDBusProxy *web_extension = NULL; + GVariant *result; + GError *error = NULL; + gboolean html_equal = FALSE; + + g_return_val_if_fail (fixture != NULL, FALSE); + g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE); + g_return_val_if_fail (html1 != NULL, FALSE); + g_return_val_if_fail (html2 != NULL, FALSE); + + cnt_editor = e_html_editor_get_content_editor (fixture->editor); + g_return_val_if_fail (cnt_editor != NULL, FALSE); + + g_object_get (cnt_editor, "web-extension", &web_extension, NULL); + + g_return_val_if_fail (G_IS_DBUS_PROXY (web_extension), FALSE); + + result = g_dbus_proxy_call_sync_wrapper ( + web_extension, + "TestHTMLEqual", + g_variant_new ("(tss)", webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (cnt_editor)), html1, html2), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + + g_clear_error (&error); + + g_return_val_if_fail (result != NULL, FALSE); + + g_variant_get (result, "(b)", &html_equal); + g_variant_unref (result); + + return html_equal; +} + +static gboolean +test_utils_process_sequence (TestFixture *fixture, + const gchar *sequence) +{ + GtkWidget *widget; + const gchar *seq; + guint keyval; + gboolean success = TRUE; + + g_return_val_if_fail (fixture != NULL, FALSE); + g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE); + g_return_val_if_fail (sequence != NULL, FALSE); + + widget = GTK_WIDGET (e_html_editor_get_content_editor (fixture->editor)); + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + for (seq = sequence; *seq && success; seq++) { + gboolean call_press = TRUE, call_release = TRUE; + guint change_state = fixture->key_state; + + switch (*seq) { + case 'S': /* Shift key press */ + keyval = GDK_KEY_Shift_L; + + if ((fixture->key_state & GDK_SHIFT_MASK) != 0) { + success = FALSE; + g_warning ("%s: Shift is already pressed", G_STRFUNC); + } else { + change_state |= GDK_SHIFT_MASK; + } + call_release = FALSE; + break; + case 's': /* Shift key release */ + keyval = GDK_KEY_Shift_L; + + if ((fixture->key_state & GDK_SHIFT_MASK) == 0) { + success = FALSE; + g_warning ("%s: Shift is already released", G_STRFUNC); + } else { + change_state &= ~GDK_SHIFT_MASK; + } + call_press = FALSE; + break; + case 'C': /* Ctrl key press */ + keyval = GDK_KEY_Control_L; + + if ((fixture->key_state & GDK_CONTROL_MASK) != 0) { + success = FALSE; + g_warning ("%s: Control is already pressed", G_STRFUNC); + } else { + change_state |= GDK_CONTROL_MASK; + } + call_release = FALSE; + break; + case 'c': /* Ctrl key release */ + keyval = GDK_KEY_Control_L; + + if ((fixture->key_state & GDK_CONTROL_MASK) == 0) { + success = FALSE; + g_warning ("%s: Control is already released", G_STRFUNC); + } else { + change_state &= ~GDK_CONTROL_MASK; + } + call_press = FALSE; + break; + case 'A': /* Alt key press */ + keyval = GDK_KEY_Alt_L; + + if ((fixture->key_state & GDK_MOD1_MASK) != 0) { + success = FALSE; + g_warning ("%s: Alt is already pressed", G_STRFUNC); + } else { + change_state |= GDK_MOD1_MASK; + } + call_release = FALSE; + break; + case 'a': /* Alt key release */ + keyval = GDK_KEY_Alt_L; + + if ((fixture->key_state & GDK_MOD1_MASK) == 0) { + success = FALSE; + g_warning ("%s: Alt is already released", G_STRFUNC); + } else { + change_state &= ~GDK_MOD1_MASK; + } + call_press = FALSE; + break; + case 'h': /* Home key press + release */ + keyval = GDK_KEY_Home; + break; + case 'e': /* End key press + release */ + keyval = GDK_KEY_End; + break; + case 'P': /* Page-Up key press + release */ + keyval = GDK_KEY_Page_Up; + break; + case 'p': /* Page-Down key press + release */ + keyval = GDK_KEY_Page_Down; + break; + case 'l': /* Arrow-Left key press + release */ + keyval = GDK_KEY_Left; + break; + case 'r': /* Arrow-Right key press + release */ + keyval = GDK_KEY_Right; + break; + case 'u': /* Arrow-Up key press + release */ + keyval = GDK_KEY_Up; + break; + case 'd': /* Arrow-Down key press + release */ + keyval = GDK_KEY_Down; + break; + case 'D': /* Delete key press + release */ + keyval = GDK_KEY_Delete; + break; + case 'b': /* Backspace key press + release */ + keyval = GDK_KEY_BackSpace; + break; + case 't': /* Tab key press + release */ + keyval = GDK_KEY_Tab; + break; + case 'n': /* Return key press + release */ + keyval = GDK_KEY_Return; + break; + default: + success = FALSE; + g_warning ("%s: Unknown sequence command '%c' in sequence '%s'", G_STRFUNC, *seq, sequence); + break; + } + + if (success) { + if (call_press) + test_utils_send_key_event (widget, GDK_KEY_PRESS, keyval, fixture->key_state); + + if (call_release) + test_utils_send_key_event (widget, GDK_KEY_RELEASE, keyval, fixture->key_state); + } + + fixture->key_state = change_state; + } + + test_utils_wait_milliseconds (event_processing_delay_ms); + + return success; +} + +static gboolean +test_utils_execute_action (TestFixture *fixture, + const gchar *action_name) +{ + GtkAction *action; + + g_return_val_if_fail (fixture != NULL, FALSE); + g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE); + g_return_val_if_fail (action_name != NULL, FALSE); + + action = e_html_editor_get_action (fixture->editor, action_name); + if (action) { + gtk_action_activate (action); + } else { + g_warning ("%s: Failed to find action '%s'", G_STRFUNC, action_name); + return FALSE; + } + + return TRUE; +} + +/* Expects only the part like "undo" [ ":" number ] */ +static gint +test_utils_maybe_extract_undo_number (const gchar *command) +{ + const gchar *ptr; + gint number; + + g_return_val_if_fail (command != NULL, -1); + + ptr = strchr (command, ':'); + if (!ptr) + return 1; + + number = atoi (ptr + 1); + g_return_val_if_fail (number > 0, -1); + + return number; +} + +static const UndoContent * +test_utils_pick_undo_content (const GSList *undo_stack, + gint number) +{ + const GSList *link; + + g_return_val_if_fail (undo_stack != NULL, NULL); + + number--; + for (link = undo_stack; link && number > 0; link = g_slist_next (link)) { + number--; + } + + g_return_val_if_fail (link != NULL, NULL); + g_return_val_if_fail (link->data != NULL, NULL); + + return link->data; +} + +/* Each line of 'commands' contains one command. + + commands = command *("\n" command) + + command = actioncmd ; Execute an action + / modecmd ; Change editor mode to HTML or Plain Text + / seqcmd ; Sequence of special key strokes + / typecmd ; Type a text + / undocmd ; Undo/redo commands + / waitcmd ; Wait command + + actioncmd = "action:" name + + actioncmd = "mode:" ("html" / "plain") + + seqcmd = "seq:" sequence + + sequence = "S" ; Shift key press + / "s" ; Shift key release + / "C" ; Ctrl key press + / "c" ; Ctrl key release + / "A" ; Alt key press + / "a" ; Alt key release + / "h" ; Home key press + release + / "e" ; End key press + release + / "P" ; Page-Up key press + release + / "p" ; Page-Down key press + release + / "l" ; Arrow-Left key press + release + / "r" ; Arrow-Right key press + release + / "u" ; Arrow-Up key press + release + / "d" ; Arrow-Down key press + release + / "D" ; Delete key press + release + / "b" ; Backspace key press + release + / "t" ; Tab key press + release + / "n" ; Return key press + release + + typecmd = "type:" text ; the 'text' can contain escaped letters with a backslash, like "\\n" transforms into "\n" + + undocmd = "undo:" undotype + + undotype = "undo" [ ":" number ] ; Call 'undo', number-times; if 'number' is not provided, then call it exactly once + / "redo" [ ":" number ] ; Call 'redo', number-times; if 'number' is not provided, then call it exactly once + / "save" ; Save current content of the editor for later tests + / "drop" [ ":" number ] ; Forgets saved content, if 'number' is provided, then top number saves are forgotten + / "test" [ ":" number ] ; Tests current editor content against any previously saved state; the optional + ; 'number' argument can be used to specify which exact previous state to use + + waitcmd = "wait:" milliseconds ; waits for 'milliseconds' + */ +gboolean +test_utils_process_commands (TestFixture *fixture, + const gchar *commands) +{ + gchar **cmds; + gint cc; + gboolean success = TRUE; + + g_return_val_if_fail (fixture != NULL, FALSE); + g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE); + g_return_val_if_fail (commands != NULL, FALSE); + + cmds = g_strsplit (commands, "\n", -1); + for (cc = 0; cmds && cmds[cc] && success; cc++) { + const gchar *command = cmds[cc]; + + if (g_str_has_prefix (command, "action:")) { + test_utils_execute_action (fixture, command + 7); + } else if (g_str_has_prefix (command, "mode:")) { + const gchar *mode_change = command + 5; + + if (g_str_equal (mode_change, "html")) { + test_utils_execute_action (fixture, "mode-html"); + } else if (g_str_equal (mode_change, "plain")) { + test_utils_execute_action (fixture, "mode-plain"); + } else { + success = FALSE; + g_warning ("%s: Unknown mode '%s'", G_STRFUNC, mode_change); + } + } else if (g_str_has_prefix (command, "seq:")) { + success = test_utils_process_sequence (fixture, command + 4); + } else if (g_str_has_prefix (command, "type:")) { + gchar *text; + + text = g_strcompress (command + 5); + success = test_utils_type_text (fixture, text); + if (!success) + g_warning ("%s: Failed to type text '%s'", G_STRFUNC, text); + g_free (text); + } else if (g_str_has_prefix (command, "undo:")) { + gint number; + + command += 5; + + if (g_str_equal (command, "undo") || g_str_has_prefix (command, "undo:")) { + number = test_utils_maybe_extract_undo_number (command); + while (number > 0 && success) { + success = test_utils_execute_action (fixture, "undo"); + number--; + } + } else if (g_str_has_prefix (command, "redo") || g_str_has_prefix (command, "redo:")) { + number = test_utils_maybe_extract_undo_number (command); + while (number > 0 && success) { + success = test_utils_execute_action (fixture, "redo"); + number--; + } + } else if (g_str_equal (command, "save")) { + UndoContent *uc; + + uc = undo_content_new (fixture); + fixture->undo_stack = g_slist_prepend (fixture->undo_stack, uc); + } else if (g_str_equal (command, "drop") || g_str_has_prefix (command, "drop:")) { + number = test_utils_maybe_extract_undo_number (command); + g_warn_if_fail (number <= g_slist_length (fixture->undo_stack)); + + while (number > 0 && fixture->undo_stack) { + UndoContent *uc = fixture->undo_stack->data; + + fixture->undo_stack = g_slist_remove (fixture->undo_stack, uc); + undo_content_free (uc); + number--; + } + } else if (g_str_equal (command, "test") || g_str_has_prefix (command, "test:")) { + const UndoContent *uc; + + number = test_utils_maybe_extract_undo_number (command); + uc = test_utils_pick_undo_content (fixture->undo_stack, number); + success = uc && undo_content_test (fixture, uc, cc); + } else { + g_warning ("%s: Unknown command 'undo:%s'", G_STRFUNC, command); + success = FALSE; + } + + test_utils_wait_milliseconds (event_processing_delay_ms); + } else if (g_str_has_prefix (command, "wait:")) { + test_utils_wait_milliseconds (atoi (command + 5)); + } else if (*command) { + g_warning ("%s: Unknown command '%s'", G_STRFUNC, command); + success = FALSE; + } + + /* Wait at least 100 ms, to give a chance to move the cursor and + other things for WebKit, for example before executing actions. */ + test_utils_wait_milliseconds (MAX (event_processing_delay_ms, 100)); + } + + g_strfreev (cmds); + + if (success) { + /* Give the editor some time to finish any ongoing async operations */ + test_utils_wait_milliseconds (MAX (event_processing_delay_ms, 100)); + } + + return success; +} + +gboolean +test_utils_run_simple_test (TestFixture *fixture, + const gchar *commands, + const gchar *expected_html, + const gchar *expected_plain) +{ + EContentEditor *cnt_editor; + gchar *text; + + g_return_val_if_fail (fixture != NULL, FALSE); + g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE); + g_return_val_if_fail (commands != NULL, FALSE); + + cnt_editor = e_html_editor_get_content_editor (fixture->editor); + + if (!test_utils_process_commands (fixture, commands)) + return FALSE; + + if (expected_html) { + text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_HTML, NULL, NULL); + g_return_val_if_fail (text != NULL, FALSE); + + if (!test_utils_html_equal (fixture, text, expected_html)) { + g_warning ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not match", G_STRFUNC, text, expected_html); + g_free (text); + return FALSE; + } + + g_free (text); + } + + if (expected_plain) { + text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_PLAIN, NULL, NULL); + g_return_val_if_fail (text != NULL, FALSE); + + if (!test_utils_html_equal (fixture, text, expected_plain)) { + g_warning ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not match", G_STRFUNC, text, expected_plain); + g_free (text); + return FALSE; + } + + g_free (text); + } + + return TRUE; +} + +void +test_utils_insert_content (TestFixture *fixture, + const gchar *content, + EContentEditorInsertContentFlags flags) +{ + EContentEditor *cnt_editor; + + g_return_if_fail (fixture != NULL); + g_return_if_fail (E_IS_HTML_EDITOR (fixture->editor)); + g_return_if_fail (content != NULL); + + cnt_editor = e_html_editor_get_content_editor (fixture->editor); + e_content_editor_insert_content (cnt_editor, content, flags); +} + +void +test_utils_set_clipboard_text (const gchar *text, + gboolean is_html) +{ + GtkClipboard *clipboard; + + g_return_if_fail (text != NULL); + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + g_return_if_fail (clipboard != NULL); + + gtk_clipboard_clear (clipboard); + + if (is_html) { + e_clipboard_set_html (clipboard, text, -1); + } else { + gtk_clipboard_set_text (clipboard, text, -1); + } +} + +gchar * +test_utils_get_clipboard_text (gboolean request_html) +{ + GtkClipboard *clipboard; + gchar *text; + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + g_return_val_if_fail (clipboard != NULL, NULL); + + if (request_html) { + g_return_val_if_fail (e_clipboard_wait_is_html_available (clipboard), NULL); + text = e_clipboard_wait_for_html (clipboard); + } else { + g_return_val_if_fail (gtk_clipboard_wait_is_text_available (clipboard), NULL); + text = gtk_clipboard_wait_for_text (clipboard); + } + + g_return_val_if_fail (text != NULL, NULL); + + return text; +} diff --git a/e-util/test-html-editor-units-utils.h b/e-util/test-html-editor-units-utils.h new file mode 100644 index 0000000..f9d045f --- /dev/null +++ b/e-util/test-html-editor-units-utils.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TEST_HTML_EDITOR_UNITS_UTILS_H +#define TEST_HTML_EDITOR_UNITS_UTILS_H + +#include <glib.h> +#include <e-util/e-util.h> + +typedef struct _TestSettings { + gchar *schema; + gchar *key; + GVariant *old_value; +} TestSettings; + +typedef struct _TestFixture { + GtkWidget *window; + EHTMLEditor *editor; + GSList *settings; /* TestSettings * */ + guint key_state; + + GSList *undo_stack; /* UndoContent * */ +} TestFixture; + +void test_utils_set_event_processing_delay_ms + (guint value); +guint test_utils_get_event_processing_delay_ms + (void); +void test_utils_fixture_set_up (TestFixture *fixture, + gconstpointer user_data); +void test_utils_fixture_tear_down (TestFixture *fixture, + gconstpointer user_data); +void test_utils_fixture_change_setting + (TestFixture *fixture, + const gchar *schema, + const gchar *key, + GVariant *value); +void test_utils_fixture_change_setting_boolean + (TestFixture *fixture, + const gchar *schema, + const gchar *key, + gboolean value); +void test_utils_fixture_change_setting_int32 + (TestFixture *fixture, + const gchar *schema, + const gchar *key, + gint value); +void test_utils_fixture_change_setting_string + (TestFixture *fixture, + const gchar *schema, + const gchar *key, + const gchar *value); +gpointer test_utils_async_call_prepare (void); +gboolean test_utils_async_call_wait (gpointer async_data, + guint timeout_seconds); +gboolean test_utils_async_call_finish (gpointer async_data); +gboolean test_utils_wait_milliseconds (guint milliseconds); +gboolean test_utils_type_text (TestFixture *fixture, + const gchar *text); +gboolean test_utils_html_equal (TestFixture *fixture, + const gchar *html1, + const gchar *html2); +gboolean test_utils_process_commands (TestFixture *fixture, + const gchar *commands); +gboolean test_utils_run_simple_test (TestFixture *fixture, + const gchar *commands, + const gchar *expected_html, + const gchar *expected_plain); +void test_utils_insert_content (TestFixture *fixture, + const gchar *content, + EContentEditorInsertContentFlags flags); +void test_utils_set_clipboard_text (const gchar *text, + gboolean is_html); +gchar * test_utils_get_clipboard_text (gboolean request_html); + +#endif /* TEST_HTML_EDITOR_UNITS_UTILS_H */ diff --git a/e-util/test-html-editor-units.c b/e-util/test-html-editor-units.c new file mode 100644 index 0000000..c72a9a2 --- /dev/null +++ b/e-util/test-html-editor-units.c @@ -0,0 +1,2823 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <locale.h> +#include <e-util/e-util.h> + +#include "e-html-editor-private.h" +#include "test-html-editor-units-utils.h" + +#define HTML_PREFIX "<html><head></head><body>" +#define HTML_PREFIX_PLAIN "<html><head></head><body style=\"font-family: Monospace;\">" +#define HTML_SUFFIX "</body></html>" + +/* The tests do not use the 'user_data' argument, thus the functions avoid them and the typecast is needed. */ +typedef void (* ETestFixtureFunc) (TestFixture *fixture, gconstpointer user_data); + +static void +test_create_editor (TestFixture *fixture) +{ + g_assert (fixture->editor != NULL); + g_assert_cmpstr (e_html_editor_get_content_editor_name (fixture->editor), ==, DEFAULT_CONTENT_EDITOR_NAME); + + /* test of the test function */ + g_assert (test_utils_html_equal (fixture, "<span>a</span>", "<sPaN>a</spaN>")); + g_assert (!test_utils_html_equal (fixture, "<span>A</span>", "<sPaN>a</spaN>")); +} + +static void +test_style_bold_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some bold text\n" + "seq:hCrcrCSrsc\n" + "action:bold\n", + HTML_PREFIX "<p>some <b>bold</b> text</p>" HTML_SUFFIX, + "some bold text")) + g_test_fail (); +} + +static void +test_style_bold_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some \n" + "action:bold\n" + "type:bold\n" + "action:bold\n" + "type: text\n", + HTML_PREFIX "<p>some <b>bold</b> text</p>" HTML_SUFFIX, + "some bold text")) + g_test_fail (); +} + +static void +test_style_italic_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some italic text\n" + "seq:hCrcrCSrsc\n" + "action:italic\n", + HTML_PREFIX "<p>some <i>italic</i> text</p>" HTML_SUFFIX, + "some italic text")) + g_test_fail (); +} + +static void +test_style_italic_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some \n" + "action:italic\n" + "type:italic\n" + "action:italic\n" + "type: text\n", + HTML_PREFIX "<p>some <i>italic</i> text</p>" HTML_SUFFIX, + "some italic text")) + g_test_fail (); +} + +static void +test_style_underline_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some underline text\n" + "seq:hCrcrCSrsc\n" + "action:underline\n", + HTML_PREFIX "<p>some <u>underline</u> text</p>" HTML_SUFFIX, + "some underline text")) + g_test_fail (); +} + +static void +test_style_underline_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some \n" + "action:underline\n" + "type:underline\n" + "action:underline\n" + "type: text\n", + HTML_PREFIX "<p>some <u>underline</u> text</p>" HTML_SUFFIX, + "some underline text")) + g_test_fail (); +} + +static void +test_style_strikethrough_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some strikethrough text\n" + "seq:hCrcrCSrsc\n" + "action:strikethrough\n", + HTML_PREFIX "<p>some <strike>strikethrough</strike> text</p>" HTML_SUFFIX, + "some strikethrough text")) + g_test_fail (); +} + +static void +test_style_strikethrough_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some \n" + "action:strikethrough\n" + "type:strikethrough\n" + "action:strikethrough\n" + "type: text\n", + HTML_PREFIX "<p>some <strike>strikethrough</strike> text</p>" HTML_SUFFIX, + "some strikethrough text")) + g_test_fail (); +} + +static void +test_style_monospace_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some monospace text\n" + "seq:hCrcrCSrsc\n" + "action:monospaced\n", + HTML_PREFIX "<p>some <font face=\"monospace\" size=\"3\">monospace</font> text</p>" HTML_SUFFIX, + "some monospace text")) + g_test_fail (); +} + +static void +test_style_monospace_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some \n" + "action:monospaced\n" + "type:monospace\n" + "action:monospaced\n" + "type: text\n", + HTML_PREFIX "<p>some <font face=\"monospace\" size=\"3\">monospace</font> text</p>" HTML_SUFFIX, + "some monospace text")) + g_test_fail (); +} + +static void +test_justify_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:center\\n\n" + "type:right\\n\n" + "type:left\\n\n" + "seq:uuu\n" + "action:justify-center\n" + "seq:d\n" + "action:justify-right\n" + "seq:d\n" + "action:justify-left\n", + HTML_PREFIX + "<p style=\"text-align: center\">center</p>" + "<p style=\"text-align: right\">right</p>" + "<p>left</p><p><br></p>" + HTML_SUFFIX, + " center\n" + " right\n" + "left\n")) + g_test_fail (); +} + +static void +test_justify_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:justify-center\n" + "type:center\\n\n" + "action:justify-right\n" + "type:right\\n\n" + "action:justify-left\n" + "type:left\\n\n", + HTML_PREFIX + "<p style=\"text-align: center\">center</p>" + "<p style=\"text-align: right\">right</p>" + "<p>left</p><p><br></p>" + HTML_SUFFIX, + " center\n" + " right\n" + "left\n")) + g_test_fail (); +} + +static void +test_indent_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:level 0\\n\n" + "type:level 1\\n\n" + "type:level 2\\n\n" + "type:level 1\\n\n" + "seq:uuu\n" + "action:indent\n" + "seq:d\n" + "action:indent\n" + "action:indent\n" + "seq:d\n" + "action:indent\n" + "action:indent\n" /* just to try whether the unindent will work too */ + "action:unindent\n", + HTML_PREFIX + "<p>level 0</p>" + "<div style=\"margin-left: 3ch;\">" + "<p>level 1</p>" + "<div style=\"margin-left: 3ch;\"><p>level 2</p></div>" + "<p>level 1</p>" + "</div><p><br></p>" + HTML_SUFFIX, + "level 0\n" + " level 1\n" + " level 2\n" + " level 1\n")) + g_test_fail (); +} + +static void +test_indent_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:level 0\\n\n" + "action:indent\n" + "type:level 1\\n\n" + "action:indent\n" + "type:level 2\\n\n" + "action:unindent\n" + "type:level 1\\n\n" + "action:unindent\n", + HTML_PREFIX + "<p>level 0</p>" + "<div style=\"margin-left: 3ch;\">" + "<p>level 1</p>" + "<div style=\"margin-left: 3ch;\"><p>level 2</p></div>" + "<p>level 1</p>" + "</div><p><br></p>" + HTML_SUFFIX, + "level 0\n" + " level 1\n" + " level 2\n" + " level 1\n")) + g_test_fail (); +} + +static void +test_font_size_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4\n" + "seq:hCSrsc\n" + "action:size-minus-two\n" + "seq:rrCSrcs\n" + "action:size-minus-one\n" + "seq:rrCSrcs\n" + "action:size-plus-zero\n" + "seq:rrCSrcs\n" + "action:size-plus-one\n" + "seq:rrCSrcs\n" + "action:size-plus-two\n" + "seq:rrCSrcs\n" + "action:size-plus-three\n" + "seq:rrCSrcs\n" + "action:size-plus-four\n", + HTML_PREFIX "<p><font size=\"1\">FontM2</font> <font size=\"2\">FontM1</font> Font0 <font size=\"4\">FontP1</font> " + "<font size=\"5\">FontP2</font> <font size=\"6\">FontP3</font> <font size=\"7\">FontP4</font></p>" HTML_SUFFIX, + "FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4")) + g_test_fail (); +} + +static void +test_font_size_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:size-minus-two\n" + "type:FontM2\n" + "action:size-plus-zero\n" + "type: \n" + "action:size-minus-one\n" + "type:FontM1\n" + "action:size-plus-zero\n" + "type: \n" + "type:Font0\n" + "action:size-plus-zero\n" + "type: \n" + "action:size-plus-one\n" + "type:FontP1\n" + "action:size-plus-zero\n" + "type: \n" + "action:size-plus-two\n" + "type:FontP2\n" + "action:size-plus-zero\n" + "type: \n" + "action:size-plus-three\n" + "type:FontP3\n" + "action:size-plus-zero\n" + "type: \n" + "action:size-plus-four\n" + "type:FontP4\n" + "action:size-plus-zero\n", + HTML_PREFIX "<p><font size=\"1\">FontM2</font> <font size=\"2\">FontM1</font> Font0 <font size=\"4\">FontP1</font> " + "<font size=\"5\">FontP2</font> <font size=\"6\">FontP3</font> <font size=\"7\">FontP4</font></p>" HTML_SUFFIX, + "FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4")) + g_test_fail (); +} + +static void +test_font_color_selection (TestFixture *fixture) +{ + EContentEditor *cnt_editor; + GdkRGBA rgba; + + g_return_if_fail (fixture != NULL); + g_return_if_fail (E_IS_HTML_EDITOR (fixture->editor)); + + cnt_editor = e_html_editor_get_content_editor (fixture->editor); + g_return_if_fail (cnt_editor != NULL); + + if (!test_utils_process_commands (fixture, + "mode:html\n" + "type:default red green blue\n" + "seq:hCrcrCSrsc\n")) { + g_test_fail (); + return; + } + + rgba.red = 1.0; + rgba.green = 0.0; + rgba.blue = 0.0; + rgba.alpha = 1.0; + + e_content_editor_set_font_color (cnt_editor, &rgba); + + if (!test_utils_process_commands (fixture, + "seq:rrCSrcs\n")) { + g_test_fail (); + return; + } + + rgba.red = 0.0; + rgba.green = 1.0; + rgba.blue = 0.0; + rgba.alpha = 1.0; + + e_content_editor_set_font_color (cnt_editor, &rgba); + + if (!test_utils_process_commands (fixture, + "seq:rrCSrcs\n")) { + g_test_fail (); + return; + } + + rgba.red = 0.0; + rgba.green = 0.0; + rgba.blue = 1.0; + rgba.alpha = 1.0; + + e_content_editor_set_font_color (cnt_editor, &rgba); + + if (!test_utils_run_simple_test (fixture, "", + HTML_PREFIX "<p>default <font color=\"#ff0000\">red</font> <font color=\"#00ff00\">green</font> " + "<font color=\"#0000ff\">blue</font></p>" HTML_SUFFIX, + "default red green blue")) + g_test_fail (); +} + +static void +test_font_color_typed (TestFixture *fixture) +{ + EContentEditor *cnt_editor; + GdkRGBA rgba; + + g_return_if_fail (fixture != NULL); + g_return_if_fail (E_IS_HTML_EDITOR (fixture->editor)); + + cnt_editor = e_html_editor_get_content_editor (fixture->editor); + g_return_if_fail (cnt_editor != NULL); + + if (!test_utils_process_commands (fixture, + "mode:html\n" + "type:default \n")) { + g_test_fail (); + return; + } + + rgba.red = 1.0; + rgba.green = 0.0; + rgba.blue = 0.0; + rgba.alpha = 1.0; + + e_content_editor_set_font_color (cnt_editor, &rgba); + + if (!test_utils_process_commands (fixture, + "type:red \n")) { + g_test_fail (); + return; + } + + rgba.red = 0.0; + rgba.green = 1.0; + rgba.blue = 0.0; + rgba.alpha = 1.0; + + e_content_editor_set_font_color (cnt_editor, &rgba); + + if (!test_utils_process_commands (fixture, + "type:green \n")) { + g_test_fail (); + return; + } + + rgba.red = 0.0; + rgba.green = 0.0; + rgba.blue = 1.0; + rgba.alpha = 1.0; + + e_content_editor_set_font_color (cnt_editor, &rgba); + + if (!test_utils_process_commands (fixture, + "type:blue\n")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, "", + HTML_PREFIX "<p>default <font color=\"#ff0000\">red </font><font color=\"#00ff00\">green </font>" + "<font color=\"#0000ff\">blue</font></p>" HTML_SUFFIX, + "default red green blue")) + g_test_fail (); +} + +static void +test_list_bullet_plain (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "action:style-list-bullet\n" + "type:item 1\\n\n" + "type:item 2\\n\n" + "type:item 3\\n\n" + "type:\\n\n" + "type:text\n", + NULL, + " * item 1\n" + " * item 2\n" + " * item 3\n" + "text")) + g_test_fail (); +} + +static void +test_list_bullet_html (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-list-bullet\n" + "type:item 1\\n\n" + "action:indent\n" + "type:item 2\\n\n" + "action:unindent\n" + "type:item 3\\n\n" + "type:\\n\n" + "type:text\n", + HTML_PREFIX + "<ul>" + "<li>item 1</li>" + "<ul>" + "<li>item 2</li>" + "</ul>" + "<li>item 3</li>" + "</ul>" + "<p>text</p>" + HTML_SUFFIX, + " * item 1\n" + " * item 2\n" + " * item 3\n" + "text")) + g_test_fail (); +} + +static void +test_list_bullet_html_from_block (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:item 1\\n\n" + "type:item 2\n" + "action:style-list-roman\n" + "type:\\n\n" + "action:style-preformat\n" + "type:item 3\\n\n" + "action:select-all\n" + "action:style-list-bullet\n", + HTML_PREFIX + "<ul>" + "<li>item 1</li>" + "<li>item 2</li>" + "<li>item 3</li>" + "<li><br></li>" + "</ul>" + HTML_SUFFIX, + " * item 1\n" + " * item 2\n" + " * item 3\n" + " * ")) + g_test_fail (); +} + +static void +test_list_alpha_html (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-list-alpha\n" + "type:item 1\\n\n" + "action:indent\n" + "type:item 2\\n\n" + "action:unindent\n" + "type:item 3\\n\n" + "type:\\n\n" + "type:text\n", + HTML_PREFIX + "<ol type=\"A\">" + "<li>item 1</li>" + "<ol type=\"A\">" + "<li>item 2</li>" + "</ol>" + "<li>item 3</li>" + "</ol>" + "<p>text</p>" + HTML_SUFFIX, + " A. item 1\n" + " A. item 2\n" + " B. item 3\n" + "text")) + g_test_fail (); +} + +static void +test_list_alpha_plain (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "action:style-list-alpha\n" + "type:item 1\\n\n" + "action:indent\n" + "type:item 2\\n\n" + "action:unindent\n" + "type:item 3\\n\n" + "type:\\n\n" + "type:text\n", + NULL, + " A. item 1\n" + " A. item 2\n" + " B. item 3\n" + "text")) + g_test_fail (); +} + +static void +test_list_roman_html (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-list-roman\n" + "type:1\\n\n" + "type:2\\n\n" + "type:3\\n\n" + "type:4\\n\n" + "type:5\\n\n" + "type:6\\n\n" + "type:7\\n\n" + "type:8\\n\n" + "type:9\\n\n" + "type:10\\n\n" + "type:11\\n\n" + "type:12\\n\n" + "type:13\\n\n" + "type:14\\n\n" + "type:15\\n\n" + "type:16\\n\n" + "type:17\\n\n" + "type:18\n", + HTML_PREFIX "<ol type=\"I\">" + "<li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li>" + "<li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li>" + "<li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li>" + "</ol>" HTML_SUFFIX, + " I. 1\n" + " II. 2\n" + " III. 3\n" + " IV. 4\n" + " V. 5\n" + " VI. 6\n" + " VII. 7\n" + "VIII. 8\n" + " IX. 9\n" + " X. 10\n" + " XI. 11\n" + " XII. 12\n" + "XIII. 13\n" + " XIV. 14\n" + " XV. 15\n" + " XVI. 16\n" + "XVII. 17\n" + "XVIII. 18")) + g_test_fail (); +} + +static void +test_list_roman_plain (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "action:style-list-roman\n" + "type:1\\n\n" + "type:2\\n\n" + "type:3\\n\n" + "type:4\\n\n" + "type:5\\n\n" + "type:6\\n\n" + "type:7\\n\n" + "type:8\\n\n" + "type:9\\n\n" + "type:10\\n\n" + "type:11\\n\n" + "type:12\\n\n" + "type:13\\n\n" + "type:14\\n\n" + "type:15\\n\n" + "type:16\\n\n" + "type:17\\n\n" + "type:18\n", + NULL, + " I. 1\n" + " II. 2\n" + " III. 3\n" + " IV. 4\n" + " V. 5\n" + " VI. 6\n" + " VII. 7\n" + "VIII. 8\n" + " IX. 9\n" + " X. 10\n" + " XI. 11\n" + " XII. 12\n" + "XIII. 13\n" + " XIV. 14\n" + " XV. 15\n" + " XVI. 16\n" + "XVII. 17\n" + "XVIII. 18")) + g_test_fail (); +} + +static void +test_list_multi_html (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-list-bullet\n" + "type:item 1\\n\n" + "type:item 2\\n\n" + "type:\\n\n" + "action:style-list-roman\n" + "type:item 3\\n\n" + "type:item 4\\n\n", + HTML_PREFIX + "<ul>" + "<li>item 1</li>" + "<li>item 2</li>" + "</ul>" + "<ol type=\"I\">" + "<li>item 3</li>" + "<li>item 4</li>" + "<li><br></li>" + "</ol>" + HTML_SUFFIX, + " * item 1\n" + " * item 2\n" + " I. item 3\n" + " II. item 4\n" + " III. ")) + g_test_fail (); +} + +static void +test_list_multi_plain (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "action:style-list-bullet\n" + "type:item 1\\n\n" + "type:item 2\\n\n" + "type:\\n\n" + "action:style-list-roman\n" + "type:item 3\\n\n" + "type:item 4\\n\n", + NULL, + " * item 1\n" + " * item 2\n" + " I. item 3\n" + " II. item 4\n" + " III. ")) + g_test_fail (); +} + +static void +test_list_multi_change_html (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-list-bullet\n" + "type:item 1\\n\n" + "type:item 2\\n\n" + "type:\\n\n" + "action:style-list-roman\n" + "type:item 3\\n\n" + "type:item 4\\n\n" + "action:select-all\n" + "action:style-list-number\n", + HTML_PREFIX + "<ol>" + "<li>item 1</li>" + "<li>item 2</li>" + "<li>item 3</li>" + "<li>item 4</li>" + "<li><br></li>" + "</ol>" + HTML_SUFFIX, + " 1. item 1\n" + " 2. item 2\n" + " 3. item 3\n" + " 4. item 4\n" + " 5. ")) + g_test_fail (); +} + +static void +test_list_multi_change_plain (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "action:style-list-bullet\n" + "type:item 1\\n\n" + "type:item 2\\n\n" + "type:\\n\n" + "action:style-list-roman\n" + "type:item 3\\n\n" + "type:item 4\\n\n" + "action:select-all\n" + "action:style-list-number\n", + NULL, + " 1. item 1\n" + " 2. item 2\n" + " 3. item 3\n" + " 4. item 4\n" + " 5. ")) + g_test_fail (); +} + +static void +test_link_insert_dialog (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:a link example: \n" + "action:insert-link\n" + "type:http://www.gnome.org\n" + "seq:n\n", + HTML_PREFIX "<p>a link example: <a href=\"http://www.gnome.org\">http://www.gnome.org</a></p>" HTML_SUFFIX, + "a link example: http://www.gnome.org")) + g_test_fail (); +} + +static void +test_link_insert_dialog_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:a link example: GNOME\n" + "seq:CSlsc\n" + "action:insert-link\n" + "type:http://www.gnome.org\n" + "seq:n\n", + HTML_PREFIX "<p>a link example: <a href=\"http://www.gnome.org\">GNOME</a></p>" HTML_SUFFIX, + "a link example: GNOME")) + g_test_fail (); +} + +static void +test_link_insert_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:www.gnome.org \n", + HTML_PREFIX "<p><a href=\"http://www.gnome.org\">www.gnome.org</a> </p>" HTML_SUFFIX, + "www.gnome.org ")) + g_test_fail (); +} + +static void +test_link_insert_typed_change_description (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:www.gnome.org \n" + "seq:ll\n" + "action:insert-link\n" + "seq:tt\n" /* Jump to the description */ + "type:GNOME\n" + "seq:n\n", + HTML_PREFIX "<p><a href=\"http://www.gnome.org\">GNOME</a> </p>" HTML_SUFFIX, + "GNOME ")) + g_test_fail (); +} + +static void +test_link_insert_dialog_remove_link (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:www.gnome.org \n" + "seq:ll\n" + "action:insert-link\n" + "seq:tttt\n" /* Jump to 'Remove Link' */ + "seq:n\n", /* Press the button */ + HTML_PREFIX "<p>www.gnome.org </p>" HTML_SUFFIX, + "www.gnome.org ")) + g_test_fail (); +} + +static void +test_link_insert_typed_append (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:www.gnome.org \n" + "seq:l\n" + "type:/about\n", + HTML_PREFIX "<p><a href=\"http://www.gnome.org/about\">www.gnome.org/about</a> </p>" HTML_SUFFIX, + "www.gnome.org/about ")) + g_test_fail (); +} + +static void +test_link_insert_typed_remove (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:www.gnome.org \n" + "seq:bbb\n", + HTML_PREFIX "<p><a href=\"http://www.gnome.org\">www.gnome.o</a></p>" HTML_SUFFIX, + "www.gnome.o")) + g_test_fail (); +} + +static void +test_h_rule_insert (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text\n" + "action:insert-rule\n" + "seq:tttttn\n", /* Move to the Close button and press it */ + HTML_PREFIX "<p>text</p><hr align=\"left\" size=\"2\" noshade=\"\">" HTML_SUFFIX, + "text")) + g_test_fail (); +} + +static void +test_h_rule_insert_text_after (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:above\n" + "action:insert-rule\n" + "seq:tttttn\n" /* Move to the Close button and press it */ + "seq:den\n" + "type:below\n", + HTML_PREFIX "<p>above</p><hr align=\"left\" size=\"2\" noshade=\"\"><p>below</p>" HTML_SUFFIX, + "above\nbelow")) + g_test_fail (); +} + +static void +test_emoticon_insert_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:before :)after\n", + HTML_PREFIX "<p>before <img src=\"data:image/png;base64," + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfA" + "hkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlu" + "a3NjYXBlLm9yZ5vuPBoAAAAXdEVYdEF1dGhvcgBMYXBvIENhbGFtYW5kcmVp35" + "EaKgAAACl0RVh0RGVzY3JpcHRpb24AQmFzZWQgb2YgSmFrdWIgU3RlaW5lciBk" + "ZXNpZ26ghAVzAAADRklEQVQ4jWWTXWiVdQCHn//7ec4+zmrbcZ+usT4wy23ilj" + "K3cmIRIl10GYGgVCMovOjjMsSrwGEFSXohMchCKBBUWEFqrQ10yTbHNh2bp03n" + "vrdztvN+/t/330U3C3/XD8/FDx6hlGLrLnzeXq0L/R1bVwfQxB4AYvW3H4kbkY" + "ouHvuyb24rL7YKej7rOJYsKv7m5Vf22eW1VUZheRqUIr+8yNLsYzk00O/nNzc/" + "fu9M/4UnBD982nat9oXdnS0HX08I/w7CWgRLAgp8E8JKIrOJvt6rXmZ8+HrXt7" + "cPA2gA33/S3lXz3K7O1vb9iWj6PFd+vcaVgQxaugyRLoMSxenvfuTu72d47eCh" + "RE3Diwe632/pAtDOn+io061k9562NxLe3XPE0QK3J/LcHllH2UmwCxAFRfw1km" + "Po3gze6FlePXQkqZt298mjLXVaKMPjzc177XjmOnHuAQJI2BoJWwcVI5QCFZOw" + "DCxLIHMZePgH+/d12FEkjxuRDDrLKrbrwUQvQsTg+xxpT6OltyG83H9PbWZ596" + "16GlPrCG+N4NEtKp49qkcy6DQCpTcWPVXB+uI0q2jUFHrsrHLRyx3ihVkEArWx" + "wZsvScIFH5mTLMxPUbwjRSC1RiOUwvCyC+ihz9OFNlf716mtlWyvd6moKsQ0BM" + "r1eTiT5d5Ejrl5l+ZSRaWlEUTKMFzXGc3cH9pba5RgyU0O7ypnaDnBz79lWd2Y" + "oyChIaRCKEFjXQmdO1OkYsnU2C183xs1vIib45P3W9OFBZrlriCyKzSlK9nd2A" + "ZFNfjuBpZdjB75+JkB5KNxfLue4fHx2JPcNGSkekamFj+qbtCTOyIF+KhonnC1" + "F19LEGOioggt2MCWEt2PeSBN+kcyfiC1Hn1gbHHpcs/X1j8rbmupuW6mlESPFF" + "ocEwcBkesifAc7DFFOQMaBS2Oak3Pj0z/dmL6kAVTkq09lA3Py8ly1M7hmMJ8L" + "8bMu5qZDgeti5F3WciF3VjUuTpY6yw6TS/rMqf+18EFLi5lPrZxUSp14piiXqE" + "n6ojLpA/DYsZh1bDWVLfIU4qtyb9sX5wYHwydqBHi7o6FJI/xQINqjWDwPoGtq" + "UqH6Ysyzv/w5PbyV/xd0ZaEGG/mx/wAAAABJRU5ErkJggg==\" alt=\":-)\">" + "after</p>" HTML_SUFFIX, + "before :-)after")) + g_test_fail (); +} + +static void +test_emoticon_insert_typed_dash (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:before :-)after\n", + HTML_PREFIX "<p>before <img src=\"data:image/png;base64," + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfA" + "hkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlu" + "a3NjYXBlLm9yZ5vuPBoAAAAXdEVYdEF1dGhvcgBMYXBvIENhbGFtYW5kcmVp35" + "EaKgAAACl0RVh0RGVzY3JpcHRpb24AQmFzZWQgb2YgSmFrdWIgU3RlaW5lciBk" + "ZXNpZ26ghAVzAAADRklEQVQ4jWWTXWiVdQCHn//7ec4+zmrbcZ+usT4wy23ilj" + "K3cmIRIl10GYGgVCMovOjjMsSrwGEFSXohMchCKBBUWEFqrQ10yTbHNh2bp03n" + "vrdztvN+/t/330U3C3/XD8/FDx6hlGLrLnzeXq0L/R1bVwfQxB4AYvW3H4kbkY" + "ouHvuyb24rL7YKej7rOJYsKv7m5Vf22eW1VUZheRqUIr+8yNLsYzk00O/nNzc/" + "fu9M/4UnBD982nat9oXdnS0HX08I/w7CWgRLAgp8E8JKIrOJvt6rXmZ8+HrXt7" + "cPA2gA33/S3lXz3K7O1vb9iWj6PFd+vcaVgQxaugyRLoMSxenvfuTu72d47eCh" + "RE3Diwe632/pAtDOn+io061k9562NxLe3XPE0QK3J/LcHllH2UmwCxAFRfw1km" + "Po3gze6FlePXQkqZt298mjLXVaKMPjzc177XjmOnHuAQJI2BoJWwcVI5QCFZOw" + "DCxLIHMZePgH+/d12FEkjxuRDDrLKrbrwUQvQsTg+xxpT6OltyG83H9PbWZ596" + "16GlPrCG+N4NEtKp49qkcy6DQCpTcWPVXB+uI0q2jUFHrsrHLRyx3ihVkEArWx" + "wZsvScIFH5mTLMxPUbwjRSC1RiOUwvCyC+ihz9OFNlf716mtlWyvd6moKsQ0BM" + "r1eTiT5d5Ejrl5l+ZSRaWlEUTKMFzXGc3cH9pba5RgyU0O7ypnaDnBz79lWd2Y" + "oyChIaRCKEFjXQmdO1OkYsnU2C183xs1vIib45P3W9OFBZrlriCyKzSlK9nd2A" + "ZFNfjuBpZdjB75+JkB5KNxfLue4fHx2JPcNGSkekamFj+qbtCTOyIF+KhonnC1" + "F19LEGOioggt2MCWEt2PeSBN+kcyfiC1Hn1gbHHpcs/X1j8rbmupuW6mlESPFF" + "ocEwcBkesifAc7DFFOQMaBS2Oak3Pj0z/dmL6kAVTkq09lA3Py8ly1M7hmMJ8L" + "8bMu5qZDgeti5F3WciF3VjUuTpY6yw6TS/rMqf+18EFLi5lPrZxUSp14piiXqE" + "n6ojLpA/DYsZh1bDWVLfIU4qtyb9sX5wYHwydqBHi7o6FJI/xQINqjWDwPoGtq" + "UqH6Ysyzv/w5PbyV/xd0ZaEGG/mx/wAAAABJRU5ErkJggg==\" alt=\":-)\">" + "after</p>" HTML_SUFFIX, + "before :-)after")) + g_test_fail (); +} + +static void +test_paragraph_normal_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-preformat\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\\n\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\n" + "seq:hu\n" + "action:style-normal\n", + HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" + "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" + "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:html\n", + HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" + "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.")) { + g_test_fail (); + return; + } +} + +static void +test_paragraph_normal_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-normal\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\\n\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\n", + HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" + "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" + "<p style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:html\n", + HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" + "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.")) { + g_test_fail (); + return; + } +} + +static void +test_paragraph_preformatted_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-normal\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\\n\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\n" + "seq:Chc\n" + "action:style-preformat\n", + HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" + "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n", + HTML_PREFIX_PLAIN "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" + "<p style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:html\n", + HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" + "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</p>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" + "odio. Praesent libero.")) { + g_test_fail (); + return; + } +} + +static void +test_paragraph_preformatted_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-preformat\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. \n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\n", + HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero." + " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. " + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n", + HTML_PREFIX_PLAIN "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero." + " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. " + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:html\n", + HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero." + " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.</pre>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. " + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.")) { + g_test_fail (); + return; + } +} + +static void +test_paragraph_address_selection (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-normal\n" + "type:normal text\\n\n" + "type:address line 1\\n\n" + "type:address line 2\\n\n" + "type:address line 3\\n\n" + "type:\\n\n" + "type:normal text\n" + "seq:huuuuSddrs\n" + "action:style-address\n", + HTML_PREFIX "<p>normal text</p>" + "<address>address line 1</address>" + "<address>address line 2</address>" + "<address>address line 3</address>" + "<p><br></p>" + "<p>normal text</p>" HTML_SUFFIX, + "normal text\n" + "address line 1\n" + "address line 2\n" + "address line 3\n" + "\n" + "normal text")) + g_test_fail (); +} + +static void +test_paragraph_address_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "action:style-normal\n" + "type:normal text\\n\n" + "action:style-address\n" + "type:address line 1\\n\n" + "type:address line 2\\n\n" + "type:address line 3\\n\n" + "action:style-normal\n" + "type:\\n\n" + "type:normal text\n", + HTML_PREFIX "<p>normal text</p>" + "<address>address line 1</address>" + "<address>address line 2</address>" + "<address>address line 3</address>" + "<p><br></p>" + "<p>normal text</p>" HTML_SUFFIX, + "normal text\n" + "address line 1\n" + "address line 2\n" + "address line 3\n" + "normal text")) + g_test_fail (); +} + +static gboolean +test_paragraph_header_n_selection (TestFixture *fixture, + gint header_n) +{ + gchar *actions, *expected_html, *expected_plain; + gboolean success; + + actions = g_strdup_printf ( + "mode:html\n" + "action:style-normal\n" + "type:normal text\\n\n" + "type:header %d\\n\n" + "type:normal text\n" + "seq:hu\n" + "action:style-h%d\n", + header_n, header_n); + + expected_html = g_strdup_printf ( + HTML_PREFIX "<p>normal text</p>" + "<h%d>header %d</h%d>" + "<p>normal text</p>" HTML_SUFFIX, + header_n, header_n, header_n); + + expected_plain = g_strdup_printf ( + "normal text\n" + "header %d\n" + "normal text", + header_n); + + success = test_utils_run_simple_test (fixture, actions, expected_html, expected_plain); + + g_free (expected_plain); + g_free (expected_html); + g_free (actions); + + if (!success) + return success; + + expected_html = g_strdup_printf ( + HTML_PREFIX "<p>normal text</p>" + "<h%d>header %d</h%d>" + "<p><br></p>" + "<p>normal text</p>" HTML_SUFFIX, + header_n, header_n, header_n); + + expected_plain = g_strdup_printf ( + "normal text\n" + "header %d\n" + "\n" + "normal text", + header_n); + + success = test_utils_run_simple_test (fixture, + "seq:h\n" + "type:\\n\n", + expected_html, expected_plain); + + g_free (expected_plain); + g_free (expected_html); + + return success; +} + +static gboolean +test_paragraph_header_n_typed (TestFixture *fixture, + gint header_n) +{ + gchar *actions, *expected_html, *expected_plain; + gboolean success; + + actions = g_strdup_printf ( + "mode:html\n" + "action:style-normal\n" + "type:normal text\\n\n" + "action:style-h%d\n" + "type:header %d\\n\n" + "action:style-normal\n" + "type:normal text\n", + header_n, header_n); + + expected_html = g_strdup_printf ( + HTML_PREFIX "<p>normal text</p>" + "<h%d>header %d</h%d>" + "<p>normal text</p>" HTML_SUFFIX, + header_n, header_n, header_n); + + expected_plain = g_strdup_printf ( + "normal text\n" + "header %d\n" + "normal text", + header_n); + + success = test_utils_run_simple_test (fixture, actions, expected_html, expected_plain); + + g_free (expected_plain); + g_free (expected_html); + g_free (actions); + + if (!success) + return success; + + expected_html = g_strdup_printf ( + HTML_PREFIX "<p>normal text</p>" + "<h%d>header %d</h%d>" + "<p><br></p>" + "<p>normal text</p>" HTML_SUFFIX, + header_n, header_n, header_n); + + expected_plain = g_strdup_printf ( + "normal text\n" + "header %d\n" + "\n" + "normal text", + header_n); + + success = test_utils_run_simple_test (fixture, + "seq:h\n" + "type:\\n\n", + expected_html, expected_plain); + + g_free (expected_plain); + g_free (expected_html); + + return success; +} + +static void +test_paragraph_header1_selection (TestFixture *fixture) +{ + if (!test_paragraph_header_n_selection (fixture, 1)) + g_test_fail (); +} + +static void +test_paragraph_header1_typed (TestFixture *fixture) +{ + if (!test_paragraph_header_n_typed (fixture, 1)) + g_test_fail (); +} + +static void +test_paragraph_header2_selection (TestFixture *fixture) +{ + if (!test_paragraph_header_n_selection (fixture, 2)) + g_test_fail (); +} + +static void +test_paragraph_header2_typed (TestFixture *fixture) +{ + if (!test_paragraph_header_n_typed (fixture, 2)) + g_test_fail (); +} + +static void +test_paragraph_header3_selection (TestFixture *fixture) +{ + if (!test_paragraph_header_n_selection (fixture, 3)) + g_test_fail (); +} + +static void +test_paragraph_header3_typed (TestFixture *fixture) +{ + if (!test_paragraph_header_n_typed (fixture, 3)) + g_test_fail (); +} + +static void +test_paragraph_header4_selection (TestFixture *fixture) +{ + if (!test_paragraph_header_n_selection (fixture, 4)) + g_test_fail (); +} + +static void +test_paragraph_header4_typed (TestFixture *fixture) +{ + if (!test_paragraph_header_n_typed (fixture, 4)) + g_test_fail (); +} + +static void +test_paragraph_header5_selection (TestFixture *fixture) +{ + if (!test_paragraph_header_n_selection (fixture, 5)) + g_test_fail (); +} + +static void +test_paragraph_header5_typed (TestFixture *fixture) +{ + if (!test_paragraph_header_n_typed (fixture, 5)) + g_test_fail (); +} + +static void +test_paragraph_header6_selection (TestFixture *fixture) +{ + if (!test_paragraph_header_n_selection (fixture, 6)) + g_test_fail (); +} + +static void +test_paragraph_header6_typed (TestFixture *fixture) +{ + if (!test_paragraph_header_n_typed (fixture, 6)) + g_test_fail (); +} + +static void +test_paragraph_wrap_lines (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\\n\n" + "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.\n" + "action:select-all\n" + "action:wrap-lines\n", + HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec<br>odio. Praesent libero.</p>" + "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec<br>odio. Praesent libero.</p>" HTML_SUFFIX, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" "odio. Praesent libero.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" "odio. Praesent libero.")) + g_test_fail (); +} + +static void +test_paste_singleline_html2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste\n" + "type: text after\n", + HTML_PREFIX "<p>text before some <b>bold</b> text text after</p>" HTML_SUFFIX, + "text before some bold text text after")) + g_test_fail (); +} + +static void +test_paste_singleline_html2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste\n" + "type: text after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before some bold text text after</p>" HTML_SUFFIX, + "text before some bold text text after")) + g_test_fail (); +} + +static void +test_paste_singleline_plain2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("some plain text", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste\n" + "type: text after\n", + HTML_PREFIX "<p>text before some plain text text after</p>" HTML_SUFFIX, + "text before some plain text text after")) + g_test_fail (); +} + +static void +test_paste_singleline_plain2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("some plain text", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste\n" + "type: text after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before some plain text text after</p>" HTML_SUFFIX, + "text before some plain text text after")) + g_test_fail (); +} + +static void +test_paste_multiline_html2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> text<br><u>underline</u> text<br></body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste\n" + "type:text after\n", + HTML_PREFIX "<p>text before <b>bold</b> text</p><p><i>italic</i> text</p><p><u>underline</u> text</p><p>text after</p>" HTML_SUFFIX, + "text before bold text\nitalic text\nunderline text\ntext after")) + g_test_fail (); +} + +static void +test_paste_multiline_html2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> text<br><u>underline</u> text</body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste\n" + "type:\\ntext after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before bold text</p>" + "<p style=\"width: 71ch;\">italic text</p>" + "<p style=\"width: 71ch;\">underline text</p>" + "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX, + "text before bold text\nitalic text\nunderline text\ntext after")) + g_test_fail (); +} + +static void +test_paste_multiline_div_html2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body><div><b>bold</b> text</div><div><i>italic</i> text</div><div><u>underline</u> text</div><div></div></body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste\n" + "type:text after\n", + HTML_PREFIX "<p>text before <b>bold</b> text</p><p><i>italic</i> text</p><p><u>underline</u> text</p><p>text after</p>" HTML_SUFFIX, + "text before bold text\nitalic text\nunderline text\ntext after")) + g_test_fail (); +} + +static void +test_paste_multiline_div_html2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body><div><b>bold</b> text</div><div><i>italic</i> text</div><div><u>underline</u> text</div></body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste\n" + "type:\\ntext after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before bold text</p>" + "<p style=\"width: 71ch;\">italic text</p>" + "<p style=\"width: 71ch;\">underline text</p>" + "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX, + "text before bold text\nitalic text\nunderline text\ntext after")) + g_test_fail (); +} + +static void +test_paste_multiline_p_html2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body><p><b>bold</b> text</p><p><i>italic</i> text</p><p><u>underline</u> text</p><p></p></body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste\n" + "type:text after\n", + HTML_PREFIX "<p>text before <b>bold</b> text</p><p><i>italic</i> text</p><p><u>underline</u> text</p><p>text after</p>" HTML_SUFFIX, + "text before bold text\nitalic text\nunderline text\ntext after")) + g_test_fail (); +} + +static void +test_paste_multiline_p_html2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body><p><b>bold</b> text</p><p><i>italic</i> text</p><p><u>underline</u> text</p></body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste\n" + "type:\\ntext after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before bold text</p>" + "<p style=\"width: 71ch;\">italic text</p>" + "<p style=\"width: 71ch;\">underline text</p>" + "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX, + "text before bold text\nitalic text\nunderline text\ntext after")) + g_test_fail (); +} + +static void +test_paste_multiline_plain2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("line 1\nline 2\nline 3\n", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste\n" + "type:text after\n", + HTML_PREFIX "<p>text before line 1</p><p>line 2</p><p>line 3</p><p>text after</p>" HTML_SUFFIX, + "text before line 1\nline 2\nline 3\ntext after")) + g_test_fail (); +} + +static void +test_paste_multiline_plain2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("line 1\nline 2\nline 3", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste\n" + "type:\\ntext after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before line 1</p>" + "<p style=\"width: 71ch;\">line 2</p>" + "<p style=\"width: 71ch;\">line 3</p>" + "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX, + "text before line 1\nline 2\nline 3\ntext after")) + g_test_fail (); +} + +static void +test_paste_quoted_singleline_html2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste-quote\n" + "type:\\n\n" /* stop quotting */ + "type:text after\n", + HTML_PREFIX "<p>text before </p>" + "<blockquote type=\"cite\"><p>some <b>bold</b> text</p></blockquote>" + "<p>text after</p>" HTML_SUFFIX, + "text before \n" + "> some bold text\n" + "text after")) + g_test_fail (); +} + +static void +test_paste_quoted_singleline_html2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste-quote\n" + "type:\\n\n" /* stop quotting */ + "type:text after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before </p>" + "<blockquote type=\"cite\"><p style=\"width: 71ch;\">> some <b>bold</b> text</p></blockquote>" + "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX, + "text before \n" + "> some bold text\n" + "text after")) + g_test_fail (); +} + +static void +test_paste_quoted_singleline_plain2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("some plain text", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste-quote\n" + "type:\\n\n" /* stop quotting */ + "type:text after\n", + HTML_PREFIX "<p>text before </p>" + "<blockquote type=\"cite\"><p>some plain text</p></blockquote>" + "<p>text after</p>" HTML_SUFFIX, + "text before \n" + "> some plain text\n" + "text after")) + g_test_fail (); +} + +static void +test_paste_quoted_singleline_plain2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("some plain text", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste-quote\n" + "type:\\n\n" /* stop quotting */ + "type:text after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before </p>" + "<blockquote type=\"cite\"><p style=\"width: 71ch;\">> some plain text</p></blockquote>" + "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX, + "text before \n" + "> some plain text\n" + "text after")) + g_test_fail (); +} + +static void +test_paste_quoted_multiline_html2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> text<br><u>underline</u> text<br></body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste-quote\n" + "seq:b\n" /* stop quotting */ + "type:text after\n", + HTML_PREFIX "<p>text before </p>" + "<blockquote type=\"cite\">> <b>bold</b> text</p>" + "<p>> <i>italic</i> text</p>" + "<p>> <u>underline</u> text</p></blockquote>" + "<p>text after</p>" HTML_SUFFIX, + "text before \n" + "> bold text\n" + "> italic text\n" + "> underline text\n" + "text after")) + g_test_fail (); +} + +static void +test_paste_quoted_multiline_html2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> text<br><u>underline</u> text</body></html>", TRUE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste-quote\n" + "type:\\n\n" /* stop quotting */ + "type:text after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before </p>" + "<blockquote type=\"cite\"><p>> bold text</p>" + "<p style=\"width: 71ch;\">> italic text</p>" + "<p style=\"width: 71ch;\">> underline text</p></blockquote>" + "<p style=\"width: 71ch;\">> text after</p>" HTML_SUFFIX, + "text before \n" + "> bold text\n" + "> italic text\n" + "> underline text\n" + "text after")) + g_test_fail (); +} + +static void +test_paste_quoted_multiline_plain2html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("line 1\nline 2\nline 3\n", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:text before \n" + "action:paste-quote\n" + "seq:b\n" /* stop quotting */ + "type:text after\n", + HTML_PREFIX "<p>text before </p>" + "<blockquote type=\"cite\"><p>line 1</p>" + "<p>line 2</p>" + "<p>line 3</p></blockquote>" + "<p>text after</p>" HTML_SUFFIX, + "text before \n" + "> line 1\n" + "> line 2\n" + "> line 3\n" + "text after")) + g_test_fail (); +} + +static void +test_paste_quoted_multiline_plain2plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("line 1\nline 2\nline 3", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:text before \n" + "action:paste-quote\n" + "type:\\n\n" /* stop quotting */ + "type:text after\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before </p>" + "<blockquote type=\"cite\"><p style=\"width: 71ch;\">> line 1</p>" + "<p style=\"width: 71ch;\">> line 2</p>" + "<p style=\"width: 71ch;\">> line 3</p></blockquote>" + "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX, + "text before\n" + "> line 1\n" + "> line 2\n" + "> line 3\n" + "text after")) + g_test_fail (); +} + +static void +test_cite_html2plain (TestFixture *fixture) +{ + if (!test_utils_process_commands (fixture, + "mode:html\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body>" + "<blockquote type=\"cite\">" + "<p>level 1</p>" + "<p><br></p>" + "<p>level 1</p>" + "<blockquote type=\"cite\">" + "<p>level 2</p>" + "</blockquote>" + "<p>back in level 1</p>" + "</blockquote>" + "<p><br></p>" + "<p>out of the citation</p>" + "</body></html>", + E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + /* Just check the content was read properly */ + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<blockquote type=\"cite\"><p>level 1</p><p><br></p><p>level 1</p>" + "<blockquote type=\"cite\"><p>level 2</p></blockquote><p>back in level 1</p></blockquote>" + "<p><br></p><p>out of the citation</p>" HTML_SUFFIX, + "> level 1\n" + ">\n" + "> level 1\n" + "> > level 2\n" + "> back in level 1\n" + "\n" + "out of the citation")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n", + HTML_PREFIX_PLAIN , + "> level 1\n" + ">\n" + "> level 1\n" + "> > level 2\n" + "> back in level 1\n" + "\n" + "out of the citation")) { + g_test_fail (); + } +} + +static void +test_cite_shortline (TestFixture *fixture) +{ + if (!test_utils_process_commands (fixture, + "mode:html\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body><blockquote type=\"cite\">" + "<p>Just one short line.</p>" + "</blockquote></body></html>", + E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<blockquote type=\"cite\">" + "<p>Just one short line.</p>" + "</blockquote>" HTML_SUFFIX, + "> Just one short line.")) { + g_test_fail (); + return; + } + + if (!test_utils_process_commands (fixture, + "seq:C\n" + "type:a\n" + "seq:cD\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body><blockquote type=\"cite\">" + "<p>Just one short line.</p>" + "</blockquote></body></html>", + E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<blockquote type=\"cite\">" + "<p>Just one short line.</p>" + "</blockquote>" HTML_SUFFIX, + "> Just one short line.")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body><blockquote type=\"cite\">" + "<p>short line 1</p>" + "<p>short line 2</p>" + "<p>short line 3</p>" + "</blockquote></body></html>", + E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<blockquote type=\"cite\">" + "<p>short line 1</p>" + "<p>short line 2</p>" + "<p>short line 3</p>" + "</blockquote>" HTML_SUFFIX, + "> short line 1\n" + "> short line 2\n" + "> short line 3")) { + g_test_fail (); + return; + } + + if (!test_utils_process_commands (fixture, + "seq:C\n" + "type:a\n" + "seq:cD\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body><blockquote type=\"cite\">" + "<p>short line 1</p>" + "<p>short line 2</p>" + "<p>short line 3</p>" + "</blockquote></body></html>", + E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<blockquote type=\"cite\">" + "<p>short line 1</p>" + "<p>short line 2</p>" + "<p>short line 3</p>" + "</blockquote>" HTML_SUFFIX, + "> short line 1\n" + "> short line 2\n" + "> short line 3")) { + g_test_fail (); + return; + } +} + +static void +test_cite_longline (TestFixture *fixture) +{ + if (!test_utils_process_commands (fixture, + "mode:html\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body><blockquote type=\"cite\">" + "<p>This is the first paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "</blockquote></body></html>", + E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<blockquote type=\"cite\">" + "<p>This is the first paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "</blockquote>" HTML_SUFFIX, + "> This is the first paragraph of a quoted text which has some long\n" + "> text to test. It has the second sentence as well.\n")) { + g_test_fail (); + return; + } + + if (!test_utils_process_commands (fixture, + "seq:C\n" + "type:a\n" + "seq:cD\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body><blockquote type=\"cite\">" + "<p>This is the first paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "</blockquote></body></html>", + E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<blockquote type=\"cite\">" + "<p>This is the first paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "</blockquote>" HTML_SUFFIX, + "> This is the first paragraph of a quoted text which has some long\n" + "> text to test. It has the second sentence as well.\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body><blockquote type=\"cite\">" + "<p>This is the first paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "<p>This is the second paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "<p>This is the third paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "</blockquote><br>after quote</body></html>", + E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<blockquote type=\"cite\">" + "<p>This is the first paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "<p>This is the second paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "<p>This is the third paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>" + "</blockquote><br>after quote" HTML_SUFFIX, + "> This is the first paragraph of a quoted text which has some long\n" + "> text to test. It has the second sentence as well.\n" + "> This is the econd paragraph of a quoted text which has some long\n" + "> text to test. It has the second sentence as well.\n" + "> This is the third paragraph of a quoted text which has some long\n" + "> text to test. It has the second sentence as well.\n" + "\nafter quote")) { + g_test_fail (); + return; + } +} + +static void +test_cite_reply_html (TestFixture *fixture) +{ + if (!test_utils_process_commands (fixture, + "mode:html\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<pre>line 1\n" + "line 2\n" + "</pre><span class=\"-x-evo-to-body\" data-credits=\"On Today, User wrote:\"></span>" + "<span class=\"-x-evo-cite-body\"></span>", + E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX "<p>On Today, User wrote:</p>" + "<blockquote type=\"cite\"><pre>line 1\n" + "line 2\n" + "</pre></blockquote>" HTML_SUFFIX, + "On Today, Use wrote:\n" + "> line 1\n" + "> line 2\n")) + g_test_fail (); + +} + +static void +test_cite_reply_plain (TestFixture *fixture) +{ + if (!test_utils_process_commands (fixture, + "mode:plain\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<pre>line 1\n" + "line 2\n" + "</pre><span class=\"-x-evo-to-body\" data-credits=\"On Today, User wrote:\"></span>" + "<span class=\"-x-evo-cite-body\"></span>", + E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">On Today, User wrote:</p>" + "<blockquote type=\"cite\"><p style=\"width: 71ch;\">> line 1</p>" + "<p style=\"width: 71ch;\">> line 2</p>" + "<p style=\"width: 71ch;\">> <br></p></blockquote>" HTML_SUFFIX, + "On Today, Use wrote:\n" + "> line 1\n" + "> line 2\n" + "> ")) + g_test_fail (); +} + +static void +test_undo_text_typed (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some te\n" + "undo:save\n" /* 1 */ + "type:tz\n" + "undo:save\n" /* 2 */ + "undo:undo\n" + "undo:undo\n" + "undo:test:2\n" + "undo:redo\n" + "undo:redo\n" + "undo:test\n" + "undo:undo:2\n" + "undo:drop\n" + "type:xt\n", + HTML_PREFIX "<p>some text</p>" HTML_SUFFIX, + "some text")) + g_test_fail (); +} + +static void +test_undo_text_forward_delete (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some text to delete\n" + "seq:hCrcrCDc\n" + "undo:undo\n" + "undo:redo\n" + "undo:undo\n", + HTML_PREFIX "<p>some text to delete</p>" HTML_SUFFIX, + "some text to delete")) + g_test_fail (); +} + +static void +test_undo_text_backward_delete (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:some text to delete\n" + "seq:hCrcrCbc\n" + "undo:undo\n" + "undo:redo\n" + "undo:undo\n", + HTML_PREFIX "<p>some text to delete</p>" HTML_SUFFIX, + "some text to delete")) + g_test_fail (); +} + +static void +test_undo_text_cut (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:some text to delete\n" + "seq:CSllsc\n" + "action:cut\n" + "undo:undo\n", + NULL, + "some text to delete")) + g_test_fail (); +} + +static void +test_undo_style (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:The first paragraph text\\n\n" + "undo:save\n" /* 1 */ + + "action:bold\n" + "type:bold\n" + "undo:save\n" /* 2 */ + "undo:undo:4\n" + "undo:test:2\n" + "undo:redo:4\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:4\n" + "type:bold\n" + "seq:CSlsc\n" + "action:bold\n" + "undo:save:\n" /* 2 */ + "undo:undo:4\n" + "undo:test:2\n" + "undo:redo:4\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:4\n" + + "action:italic\n" + "type:italic\n" + "undo:save\n" /* 2 */ + "undo:undo:6\n" + "undo:test:2\n" + "undo:redo:6\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:6\n" + "type:italic\n" + "seq:CSlsc\n" + "action:italic\n" + "undo:save:\n" /* 2 */ + "undo:undo:6\n" + "undo:test:2\n" + "undo:redo:6\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:6\n" + + "action:underline\n" + "type:underline\n" + "undo:save\n" /* 2 */ + "undo:undo:9\n" + "undo:test:2\n" + "undo:redo:9\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:9\n" + "type:italic\n" + "seq:CSlsc\n" + "action:underline\n" + "undo:save:\n" /* 2 */ + "undo:undo:9\n" + "undo:test:2\n" + "undo:redo:9\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:9\n" + + "action:strikethrough\n" + "type:strikethrough\n" + "undo:save\n" /* 2 */ + "undo:undo:13\n" + "undo:test:2\n" + "undo:redo:13\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:13\n" + "type:strikethrough\n" + "seq:CSlsc\n" + "action:strikethrough\n" + "undo:save:\n" /* 2 */ + "undo:undo:13\n" + "undo:test:2\n" + "undo:redo:13\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:13\n" + + "action:monospaced\n" + "type:monospaced\n" + "undo:save\n" /* 2 */ + "undo:undo:10\n" + "undo:test:2\n" + "undo:redo:10\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:10\n" + "type:monospaced\n" + "seq:CSlsc\n" + "action:monospaced\n" + "undo:save:\n" /* 2 */ + "undo:undo:10\n" + "undo:test:2\n" + "undo:redo:10\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:10\n", + HTML_PREFIX "<p>The first paragraph</p><p><br></p>" HTML_SUFFIX, + "The first paragraph\n")) + g_test_fail (); +} + +static void +test_undo_justify (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:The first paragraph text\\n\n" + "undo:save\n" /* 1 */ + + "action:justify-left\n" + "type:left\n" + "undo:save\n" /* 2 */ + "undo:undo:4\n" + "undo:test:2\n" + "undo:redo:4\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:4\n" + "type:left\n" + "seq:CSlsc\n" + "action:justify-left\n" + "undo:save:\n" /* 2 */ + "undo:undo:4\n" + "undo:test:2\n" + "undo:redo:4\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:4\n" + + "action:justify-center\n" + "type:center\n" + "undo:save\n" /* 2 */ + "undo:undo:6\n" + "undo:test:2\n" + "undo:redo:6\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:6\n" + "type:center\n" + "seq:CSlsc\n" + "action:justify-center\n" + "undo:save:\n" /* 2 */ + "undo:undo:6\n" + "undo:test:2\n" + "undo:redo:6\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:6\n" + + "action:justify-right\n" + "type:right\n" + "undo:save\n" /* 2 */ + "undo:undo:5\n" + "undo:test:2\n" + "undo:redo:5\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:5\n" + "type:right\n" + "seq:CSlsc\n" + "action:justify-right\n" + "undo:save:\n" /* 2 */ + "undo:undo:5\n" + "undo:test:2\n" + "undo:redo:5\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:5\n", + + HTML_PREFIX "<p>The first paragraph</p><p><br></p>" HTML_SUFFIX, + "The first paragraph\n")) + g_test_fail (); +} + +static void +test_undo_indent (TestFixture *fixture) +{ + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:The first paragraph text\\n\n" + "undo:save\n" /* 1 */ + + "action:indent\n" + "type:text\n" + "undo:save\n" /* 2 */ + "undo:undo:5\n" + "undo:test:2\n" + "undo:redo:5\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:5\n" + "type:text\n" + "seq:CSlsc\n" + "action:indent\n" + "undo:save:\n" /* 2 */ + "undo:undo:5\n" + "undo:test:2\n" + "undo:redo:5\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:5\n" + + "type:text\n" + "undo:save\n" /* 2 */ + "action:indent\n" + "undo:save\n" /* 3 */ + "action:unindent\n" + "undo:test:2\n" + "action:indent\n" + "undo:test\n" + "undo:save\n" /* 4 */ + "undo:undo:2\n" + "undo:test:2\n" + "undo:redo:2\n" + "undo:test\n" + "undo:drop:2\n" /* drop the save 4 and 3 */ + "undo:undo:3\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:4\n" + "undo:test\n" + + "type:level 1\\n\n" + "type:level 2\\n\n" + "type:level 3\\n\n" + "seq:uuu\n" + "action:indent\n" + "undo:save\n" /* 2 */ + "seq:d\n" + "action:indent\n" + "action:indent\n" + "undo:save\n" /* 3 */ + "undo:undo:2\n" + "undo:test:2\n" + "undo:redo:2\n" + "undo:test\n" + "undo:drop:2\n" /* drop the save 3 and 2 */ + "seq:d\n" + + "action:indent\n" + "undo:save\n" /* 2 */ + "action:indent\n" + "action:indent\n" + "undo:save\n" /* 3 */ + "undo:undo:2\n" + "undo:test:2\n" + "undo:redo:2\n" + "undo:test\n" + "undo:drop:2\n" /* drop the save 3 and 2 */ + + "undo:save\n" /* 2 */ + "undo:undo:30\n" /* 6x action:indent, 24x type "level X\\n" */ + "undo:test:2\n" + "undo:redo:30\n" + "undo:test\n" + "undo:drop\n" /* drop the save 2 */ + "undo:undo:30\n", + + HTML_PREFIX "<p>The first paragraph</p><p><br></p>" HTML_SUFFIX, + "The first paragraph\n")) + g_test_fail (); +} + +static void +test_undo_link_paste_html (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("http://www.gnome.org", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:html\n" + "type:URL:\\n\n" + "undo:save\n" /* 1 */ + "action:paste\n" + "type:\\n\n" + "undo:save\n" /* 2 */ + "undo:undo:2\n" + "undo:test:2\n" + "undo:undo:5\n" + "undo:redo:7\n" + "undo:test\n", + HTML_PREFIX "<p>URL:</p><p><a href=\"http://www.gnome.org\">http://www.gnome.org</a></p><p><br></p>" HTML_SUFFIX, + "URL:\nhttp://www.gnome.org\n")) + g_test_fail (); +} + +static void +test_undo_link_paste_plain (TestFixture *fixture) +{ + test_utils_set_clipboard_text ("http://www.gnome.org", FALSE); + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:URL:\\n\n" + "undo:save\n" /* 1 */ + "action:paste\n" + "type:\\n\n" + "undo:save\n" /* 2 */ + "undo:undo:2\n" + "undo:test:2\n" + "undo:undo:5\n" + "undo:redo:7\n" + "undo:test\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">URL:</p>" + "<p style=\"width: 71ch;\"><a href=\"http://www.gnome.org\">http://www.gnome.org</a></p>" + "<p style=\"width: 71ch;\"><br></p>" HTML_SUFFIX, + "URL:\nhttp://www.gnome.org\n")) + g_test_fail (); +} + +static void +test_bug_726548 (TestFixture *fixture) +{ + gboolean success; + gchar *text; + const gchar *expected_plain = + "aaa\n" + " 1. a\n" + " 2. b\n" + " 3. c\n"; + + if (!test_utils_run_simple_test (fixture, + "mode:plain\n" + "type:aaa\\n\n" + "action:style-list-number\n" + "type:a\\nb\\nc\\n\\n\n" + "seq:C\n" + "type:ac\n" + "seq:c\n", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">aaa</p>" + "<ol data-evo-paragraph=\"\" style=\"width: 65ch;\">" + "<li>a</li><li>b</li><li>c</li></ol>" + "<p style=\"width: 71ch;\"><br></p>" HTML_SUFFIX, + expected_plain)) { + g_test_fail (); + return; + } + + text = test_utils_get_clipboard_text (FALSE); + success = test_utils_html_equal (fixture, text, expected_plain); + + if (!success) { + g_warning ("%s: clipboard Plain text \n---%s---\n does not match expected Plain\n---%s---", + G_STRFUNC, text, expected_plain); + g_free (text); + g_test_fail (); + } else { + g_free (text); + } +} + +static void +test_bug_750657 (TestFixture *fixture) +{ + if (!test_utils_process_commands (fixture, + "mode:html\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body>\n" + "<blockquote type=\"cite\">\n" + "<p>This is the first paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>\n" + "<p><br></p>\n" + "<p>This is the third paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>\n" + "<blockquote type=\"cite\">\n" + "<p>This is the first paragraph of a sub-quoted text which has some long text to test. It has the second sentence as well.</p>\n" + "<br>\n" + "</blockquote>\n" + "<p>This is the fourth paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>\n" + "</blockquote>\n" + "<p><br></p>\n" + "</body></html>\n", + E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "seq:uuuSuusD\n", + HTML_PREFIX "\n" + "<blockquote type=\"cite\">\n" + "<p>This is the first paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>\n" + "<p><br></p>\n" + "<p>This is the third paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>\n" + "<p>This is the fourth paragraph of a quoted text which has some long text to test. It has the second sentence as well.</p>\n" + "</blockquote>\n" + "<p><br></p>\n" + HTML_SUFFIX, + NULL)) { + g_test_fail (); + return; + } +} + +static void +test_bug_760989 (TestFixture *fixture) +{ + if (!test_utils_process_commands (fixture, + "mode:html\n" + "type:a\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head></head><body>\n" + "One line before quotation<br>\n" + "<blockquote type=\"cite\">\n" + "<p>Single line quoted.</p>\n" + "</blockquote>\n" + "</body></html>", + E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_run_simple_test (fixture, + "seq:ChcD\n", + HTML_PREFIX "<p>One line before quotation</p>\n" + "<blockquote type=\"cite\">\n" + "<p>Single line quoted.</p>\n" + "</blockquote>" HTML_SUFFIX, + "One line before quotation\n" + "> Single line quoted.")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "seq:Cecb\n", + HTML_PREFIX "<p>One line before quotation</p>\n" + "<blockquote type=\"cite\">\n" + "<p>Single line quoted</p>\n" + "</blockquote>" HTML_SUFFIX, + "One line before quotation\n" + "> Single line quoted")) { + g_test_fail (); + return; + } +} + +static void +test_bug_769708 (TestFixture *fixture) +{ + if (!test_utils_process_commands (fixture, + "mode:html\n")) { + g_test_fail (); + return; + } + + test_utils_insert_content (fixture, + "<html><head><style id=\"-x-evo-quote-style\" type=\"text/css\">.-x-evo-quoted { -webkit-user-select: none; }</style>" + "<style id=\"-x-evo-style-a\" type=\"text/css\">a { cursor: text; }</style></head>" + "<body data-evo-plain-text=\"\" spellcheck=\"true\">" + "<p data-evo-paragraph=\"\" class=\"\" id=\"-x-evo-input-start\">aaa</p>" + "<div class=\"-x-evo-signature-wrapper\"><span class=\"-x-evo-signature\" id=\"autogenerated\"><pre>-- <br></pre>" + "<p data-evo-paragraph=\"\" class=\"\">user <user@no.where></p>" + "</span></div></body></html>", + E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + if (!test_utils_process_commands (fixture, + "mode:plain\n")) { + g_test_fail (); + return; + } + + if (!test_utils_run_simple_test (fixture, + "", + HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">aaa</p><div><span><p style=\"width: 71ch;\">-- </p>" + "<p style=\"width: 71ch;\"<br></p><p style=\"width: 71ch;\">user <user@no.where></p></span></div>" HTML_SUFFIX, + "aaa\n" + "-- \n" + "user <user@no.where>")) + g_test_fail (); +} + +gint +main (gint argc, + gchar *argv[]) +{ + gint cmd_delay = -1; + GOptionEntry entries[] = { + { "cmd-delay", '\0', 0, + G_OPTION_ARG_INT, &cmd_delay, + "Specify delay, in milliseconds, to use during processing commands. Default is 5 ms.", + NULL }, + { NULL } + }; + GOptionContext *context; + GError *error = NULL; + GList *modules; + gint res; + + setlocale (LC_ALL, ""); + + /* Force the memory GSettings backend, to not overwrite user settings + when playing with them. It also ensures that the test will run with + default settings, until changed. */ + g_setenv ("GSETTINGS_BACKEND", "memory", TRUE); + + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id="); + + gtk_init (&argc, &argv); + + context = g_option_context_new (NULL); + g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_warning ("Failed to parse arguments: %s\n", error ? error->message : "Unknown error"); + g_option_context_free (context); + g_clear_error (&error); + return -1; + } + + g_option_context_free (context); + + if (cmd_delay > 0) + test_utils_set_event_processing_delay_ms ((guint) cmd_delay); + + e_util_init_main_thread (NULL); + e_passwords_init (); + + modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR); + g_list_free_full (modules, (GDestroyNotify) g_type_module_unuse); + + #define add_test(_name, _func) \ + g_test_add (_name, TestFixture, NULL, \ + test_utils_fixture_set_up, (ETestFixtureFunc) _func, test_utils_fixture_tear_down) + + add_test ("/create/editor", test_create_editor); + add_test ("/style/bold/selection", test_style_bold_selection); + add_test ("/style/bold/typed", test_style_bold_typed); + add_test ("/style/italic/selection", test_style_italic_selection); + add_test ("/style/italic/typed", test_style_italic_typed); + add_test ("/style/underline/selection", test_style_underline_selection); + add_test ("/style/underline/typed", test_style_underline_typed); + add_test ("/style/strikethrough/selection", test_style_strikethrough_selection); + add_test ("/style/strikethrough/typed", test_style_strikethrough_typed); + add_test ("/style/monospace/selection", test_style_monospace_selection); + add_test ("/style/monospace/typed", test_style_monospace_typed); + add_test ("/justify/selection", test_justify_selection); + add_test ("/justify/typed", test_justify_typed); + add_test ("/indent/selection", test_indent_selection); + add_test ("/indent/typed", test_indent_typed); + add_test ("/font/size/selection", test_font_size_selection); + add_test ("/font/size/typed", test_font_size_typed); + add_test ("/font/color/selection", test_font_color_selection); + add_test ("/font/color/typed", test_font_color_typed); + add_test ("/list/bullet/plain", test_list_bullet_plain); + add_test ("/list/bullet/html", test_list_bullet_html); + add_test ("/list/bullet/html/from-block", test_list_bullet_html_from_block); + add_test ("/list/alpha/html", test_list_alpha_html); + add_test ("/list/alpha/plain", test_list_alpha_plain); + add_test ("/list/roman/html", test_list_roman_html); + add_test ("/list/roman/plain", test_list_roman_plain); + add_test ("/list/multi/html", test_list_multi_html); + add_test ("/list/multi/plain", test_list_multi_plain); + add_test ("/list/multi/change/html", test_list_multi_change_html); + add_test ("/list/multi/change/plain", test_list_multi_change_plain); + add_test ("/link/insert/dialog", test_link_insert_dialog); + add_test ("/link/insert/dialog/selection", test_link_insert_dialog_selection); + add_test ("/link/insert/dialog/remove-link", test_link_insert_dialog_remove_link); + add_test ("/link/insert/typed", test_link_insert_typed); + add_test ("/link/insert/typed/change-description", test_link_insert_typed_change_description); + add_test ("/link/insert/typed/append", test_link_insert_typed_append); + add_test ("/link/insert/typed/remove", test_link_insert_typed_remove); + add_test ("/h-rule/insert", test_h_rule_insert); + add_test ("/h-rule/insert-text-after", test_h_rule_insert_text_after); + add_test ("/emoticon/insert/typed", test_emoticon_insert_typed); + add_test ("/emoticon/insert/typed-dash", test_emoticon_insert_typed_dash); + add_test ("/paragraph/normal/selection", test_paragraph_normal_selection); + add_test ("/paragraph/normal/typed", test_paragraph_normal_typed); + add_test ("/paragraph/preformatted/selection", test_paragraph_preformatted_selection); + add_test ("/paragraph/preformatted/typed", test_paragraph_preformatted_typed); + add_test ("/paragraph/address/selection", test_paragraph_address_selection); + add_test ("/paragraph/address/typed", test_paragraph_address_typed); + add_test ("/paragraph/header1/selection", test_paragraph_header1_selection); + add_test ("/paragraph/header1/typed", test_paragraph_header1_typed); + add_test ("/paragraph/header2/selection", test_paragraph_header2_selection); + add_test ("/paragraph/header2/typed", test_paragraph_header2_typed); + add_test ("/paragraph/header3/selection", test_paragraph_header3_selection); + add_test ("/paragraph/header3/typed", test_paragraph_header3_typed); + add_test ("/paragraph/header4/selection", test_paragraph_header4_selection); + add_test ("/paragraph/header4/typed", test_paragraph_header4_typed); + add_test ("/paragraph/header5/selection", test_paragraph_header5_selection); + add_test ("/paragraph/header5/typed", test_paragraph_header5_typed); + add_test ("/paragraph/header6/selection", test_paragraph_header6_selection); + add_test ("/paragraph/header6/typed", test_paragraph_header6_typed); + add_test ("/paragraph/wrap-lines", test_paragraph_wrap_lines); + add_test ("/paste/singleline/html2html", test_paste_singleline_html2html); + add_test ("/paste/singleline/html2plain", test_paste_singleline_html2plain); + add_test ("/paste/singleline/plain2html", test_paste_singleline_plain2html); + add_test ("/paste/singleline/plain2plain", test_paste_singleline_plain2plain); + add_test ("/paste/multiline/html2html", test_paste_multiline_html2html); + add_test ("/paste/multiline/html2plain", test_paste_multiline_html2plain); + add_test ("/paste/multiline/div/html2html", test_paste_multiline_div_html2html); + add_test ("/paste/multiline/div/html2plain", test_paste_multiline_div_html2plain); + add_test ("/paste/multiline/p/html2html", test_paste_multiline_p_html2html); + add_test ("/paste/multiline/p/html2plain", test_paste_multiline_p_html2plain); + add_test ("/paste/multiline/plain2html", test_paste_multiline_plain2html); + add_test ("/paste/multiline/plain2plain", test_paste_multiline_plain2plain); + add_test ("/paste/quoted/singleline/html2html", test_paste_quoted_singleline_html2html); + add_test ("/paste/quoted/singleline/html2plain", test_paste_quoted_singleline_html2plain); + add_test ("/paste/quoted/singleline/plain2html", test_paste_quoted_singleline_plain2html); + add_test ("/paste/quoted/singleline/plain2plain", test_paste_quoted_singleline_plain2plain); + add_test ("/paste/quoted/multiline/html2html", test_paste_quoted_multiline_html2html); + add_test ("/paste/quoted/multiline/html2plain", test_paste_quoted_multiline_html2plain); + add_test ("/paste/quoted/multiline/plain2html", test_paste_quoted_multiline_plain2html); + add_test ("/paste/quoted/multiline/plain2plain", test_paste_quoted_multiline_plain2plain); + add_test ("/cite/html2plain", test_cite_html2plain); + add_test ("/cite/shortline", test_cite_shortline); + add_test ("/cite/longline", test_cite_longline); + add_test ("/cite/reply/html", test_cite_reply_html); + add_test ("/cite/reply/plain", test_cite_reply_plain); + add_test ("/undo/text/typed", test_undo_text_typed); + add_test ("/undo/text/forward-delete", test_undo_text_forward_delete); + add_test ("/undo/text/backward-delete", test_undo_text_backward_delete); + add_test ("/undo/text/cut", test_undo_text_cut); + add_test ("/undo/style", test_undo_style); + add_test ("/undo/justify", test_undo_justify); + add_test ("/undo/indent", test_undo_indent); + add_test ("/undo/link-paste/html", test_undo_link_paste_html); + add_test ("/undo/link-paste/plain", test_undo_link_paste_plain); + add_test ("/bug/726548", test_bug_726548); + add_test ("/bug/750657", test_bug_750657); + add_test ("/bug/760989", test_bug_760989); + add_test ("/bug/769708", test_bug_769708); + + #undef add_test + + res = g_test_run (); + + e_util_cleanup_settings (); + e_spell_checker_free_global_memory (); + + return res; +} diff --git a/e-util/test-html-editor.c b/e-util/test-html-editor.c index e393f87..86502c4 100644 --- a/e-util/test-html-editor.c +++ b/e-util/test-html-editor.c @@ -27,15 +27,25 @@ #include <glib/gi18n-lib.h> +/* Enable it, once printing is implemented (it doesn't work to do it + on a WebKit side, because the EContentEditor can be a different + structure. That might be why EMsgComposer uses a "print" signal, + which prints a constructed message, like within the message preview. */ +/* #define ENABLE_PRINT */ + static const gchar *file_ui = "<ui>\n" " <menubar name='main-menu'>\n" " <menu action='file-menu'>\n" +" <menuitem action='new-editor'/>\n" +" <separator/>\n" " <menuitem action='save'/>\n" " <menuitem action='save-as'/>\n" +#ifdef ENABLE_PRINT " <separator/>\n" " <menuitem action='print-preview'/>\n" " <menuitem action='print'/>\n" +#endif /* ENABLE_PRINT */ " <separator/>\n" " <menuitem action='disable-editor'/>\n" " <separator/>\n" @@ -51,11 +61,14 @@ static const gchar *view_ui = " <menuitem action='view-html-output'/>\n" " <menuitem action='view-html-source'/>\n" " <menuitem action='view-plain-source'/>\n" -" <menuitem action='view-inspector'/>\n" +" <separator/>\n" +" <menuitem action='view-webkit-inspector'/>\n" " </menu>\n" " </menubar>\n" "</ui>"; +static void create_new_editor (void); + static void handle_error (GError **error) { @@ -65,7 +78,8 @@ handle_error (GError **error) } } -static GtkPrintOperationResult +#ifdef ENABLE_PRINT +static void print (EHTMLEditor *editor, GtkPrintOperationAction action) { @@ -78,13 +92,12 @@ print (EHTMLEditor *editor, frame = webkit_web_view_get_main_frame ( WEBKIT_WEB_VIEW (e_html_editor_get_view (editor))); - result = webkit_web_frame_print_full (frame, operation, action, NULL); + webkit_web_frame_print_full (frame, operation, action, NULL); g_object_unref (operation); handle_error (&error); - - return result; } +#endif static gint save_dialog (EHTMLEditor *editor) @@ -139,8 +152,11 @@ view_source_dialog (EHTMLEditor *editor, GtkWidget *content; GtkWidget *content_area; GtkWidget *scrolled_window; + EContentEditor *cnt_editor; gchar * html; + cnt_editor = e_html_editor_get_content_editor (editor); + dialog = gtk_dialog_new_with_buttons ( title, GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))), @@ -165,15 +181,17 @@ view_source_dialog (EHTMLEditor *editor, gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 300); if (plain_text) { - html = e_html_editor_view_get_text_plain ( - e_html_editor_get_view (editor)); + html = e_content_editor_get_content (cnt_editor, + E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_PLAIN, + NULL, NULL); } else { - GList *inline_images; + GSList *inline_images = NULL; - html = e_html_editor_view_get_text_html ( - e_html_editor_get_view (editor), "test-domain", &inline_images); + html = e_content_editor_get_content (cnt_editor, + E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_HTML | E_CONTENT_EDITOR_GET_INLINE_IMAGES, + "test-domain", &inline_images); - g_list_free_full (inline_images, g_object_unref); + g_slist_free_full (inline_images, g_object_unref); } if (show_source || plain_text) { @@ -185,8 +203,7 @@ view_source_dialog (EHTMLEditor *editor, gtk_text_view_set_editable (GTK_TEXT_VIEW (content), FALSE); } else { content = webkit_web_view_new (); - webkit_web_view_load_string ( - WEBKIT_WEB_VIEW (content), html, NULL, NULL, NULL); + webkit_web_view_load_html (WEBKIT_WEB_VIEW (content), html, "evo-file://"); } g_free (html); @@ -198,6 +215,14 @@ view_source_dialog (EHTMLEditor *editor, } static void +action_new_editor_cb (GtkAction *action, + EHTMLEditor *editor) +{ + create_new_editor (); +} + +#ifdef ENABLE_PRINT +static void action_print_cb (GtkAction *action, EHTMLEditor *editor) { @@ -210,6 +235,7 @@ action_print_preview_cb (GtkAction *action, { print (editor, GTK_PRINT_OPERATION_ACTION_PREVIEW); } +#endif /* ENABLE_PRINT */ static void action_quit_cb (GtkAction *action, @@ -231,7 +257,7 @@ action_save_cb (GtkAction *action, return; filename = e_html_editor_get_filename (editor); - as_html = (e_html_editor_view_get_html_mode (e_html_editor_get_view (editor))); + as_html = (e_content_editor_get_html_mode (e_html_editor_get_content_editor (editor))); e_html_editor_save (editor, filename, as_html, &error); handle_error (&error); @@ -249,7 +275,7 @@ action_save_as_cb (GtkAction *action, return; filename = e_html_editor_get_filename (editor); - as_html = (e_html_editor_view_get_html_mode (e_html_editor_get_view (editor))); + as_html = (e_content_editor_get_html_mode (e_html_editor_get_content_editor (editor))); e_html_editor_save (editor, filename, as_html, &error); handle_error (&error); @@ -259,12 +285,10 @@ static void action_toggle_editor (GtkAction *action, EHTMLEditor *editor) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; - view = e_html_editor_get_view (editor); - webkit_web_view_set_editable ( - WEBKIT_WEB_VIEW (view), - ! webkit_web_view_get_editable (WEBKIT_WEB_VIEW (view))); + cnt_editor = e_html_editor_get_content_editor (editor); + e_content_editor_set_editable (cnt_editor, !e_content_editor_is_editable (cnt_editor)); } static void @@ -293,16 +317,26 @@ action_view_inspector (GtkAction *action, EHTMLEditor *editor) { WebKitWebInspector *inspector; - EHTMLEditorView *view; + EContentEditor *cnt_editor; - view = e_html_editor_get_view (editor); - inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view)); - - webkit_web_inspector_show (inspector); + cnt_editor = e_html_editor_get_content_editor (editor); + if (WEBKIT_IS_WEB_VIEW (cnt_editor)) { + inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (cnt_editor)); + webkit_web_inspector_show (inspector); + } else { + g_print ("Cannot show the inspector, the content editor is not a WebKitWebView descendant\n"); + } } static GtkActionEntry file_entries[] = { + { "new-editor", + "document-new", + N_("_New editor"), + "<Control>N", + NULL, + G_CALLBACK (action_new_editor_cb) }, +#ifdef ENABLE_PRINT { "print", "document-print", N_("_Print..."), @@ -316,6 +350,7 @@ static GtkActionEntry file_entries[] = { "<Control><Shift>p", NULL, G_CALLBACK (action_print_preview_cb) }, +#endif /* ENABLE_PRINT */ { "quit", "application-exit", @@ -376,11 +411,11 @@ static GtkActionEntry view_entries[] = { NULL, G_CALLBACK (action_view_plain_source) }, - { "view-inspector", + { "view-webkit-inspector", NULL, N_("Inspector"), NULL, - NULL, + "<Control><Shift>I", G_CALLBACK (action_view_inspector) }, { "view-menu", @@ -391,53 +426,64 @@ static GtkActionEntry view_entries[] = { NULL } }; -static WebKitWebView * -open_inspector (WebKitWebInspector *inspector, - WebKitWebView *webview, - gpointer user_data) -{ - GtkWidget *window; - GtkWidget *inspector_view; - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - inspector_view = webkit_web_view_new (); +static guint glob_editors = 0; - gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (inspector_view)); +static void +editor_destroyed_cb (GtkWidget *editor) +{ + g_return_if_fail (glob_editors > 0); - gtk_widget_set_size_request (window, 600, 480); - gtk_widget_show (window); + glob_editors--; - return WEBKIT_WEB_VIEW (inspector_view); + if (!glob_editors) + gtk_main_quit (); } -gint -main (gint argc, - gchar **argv) +static void +create_new_editor_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { GtkActionGroup *action_group; GtkUIManager *manager; GtkWidget *container; GtkWidget *widget; EHTMLEditor *editor; - EHTMLEditorView *view; - WebKitWebInspector *inspector; - + EContentEditor *cnt_editor; GError *error = NULL; - bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); + widget = e_html_editor_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message); + g_clear_error (&error); + editor_destroyed_cb (NULL); + return; + } - gtk_init (&argc, &argv); + editor = E_HTML_EDITOR (widget); + cnt_editor = e_html_editor_get_content_editor (editor); + + g_object_set (G_OBJECT (editor), + "halign", GTK_ALIGN_FILL, + "hexpand", TRUE, + "valign", GTK_ALIGN_FILL, + "vexpand", TRUE, + NULL); - editor = g_object_ref_sink (e_html_editor_new ()); - view = e_html_editor_get_view (editor); + g_object_set (G_OBJECT (cnt_editor), + "halign", GTK_ALIGN_FILL, + "hexpand", TRUE, + "valign", GTK_ALIGN_FILL, + "vexpand", TRUE, + NULL); + + if (WEBKIT_IS_WEB_VIEW (cnt_editor)) { + WebKitSettings *web_settings; - inspector = webkit_web_view_get_inspector ( - WEBKIT_WEB_VIEW (view)); - g_signal_connect ( - inspector, "inspect-web-view", - G_CALLBACK (open_inspector), NULL); + web_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (cnt_editor)); + webkit_settings_set_allow_file_access_from_file_urls (web_settings, TRUE); + webkit_settings_set_enable_developer_extras (web_settings, TRUE); + } widget = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_set_size_request (widget, 600, 400); @@ -445,7 +491,7 @@ main (gint argc, g_signal_connect_swapped ( widget, "destroy", - G_CALLBACK (gtk_main_quit), NULL); + G_CALLBACK (editor_destroyed_cb), NULL); container = widget; @@ -467,6 +513,8 @@ main (gint argc, gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); gtk_widget_show (widget); + gtk_widget_grab_focus (GTK_WIDGET (cnt_editor)); + manager = e_html_editor_get_ui_manager (editor); gtk_ui_manager_add_ui_from_string (manager, file_ui, -1, &error); @@ -491,16 +539,48 @@ main (gint argc, G_N_ELEMENTS (view_entries), editor); gtk_ui_manager_insert_action_group (manager, action_group, 0); + if (!WEBKIT_IS_WEB_VIEW (cnt_editor)) { + GtkAction *action; + + action = e_html_editor_get_action (editor, "view-webkit-inspector"); + gtk_action_set_visible (action, FALSE); + } + gtk_ui_manager_ensure_update (manager); +} + +static void +create_new_editor (void) +{ + glob_editors++; + + e_html_editor_new (create_new_editor_cb, NULL); +} + +gint +main (gint argc, + gchar **argv) +{ + GList *modules; + + bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init (&argc, &argv); + + e_util_init_main_thread (NULL); + e_passwords_init (); + + modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR); + g_list_free_full (modules, (GDestroyNotify) g_type_module_unuse); - g_signal_connect ( - editor, "destroy", - G_CALLBACK (gtk_main_quit), NULL); + create_new_editor (); gtk_main (); - g_object_unref (editor); e_util_cleanup_settings (); + e_spell_checker_free_global_memory (); return 0; } diff --git a/em-format/Makefile.am b/em-format/Makefile.am index 7827d79..3f2c438 100644 --- a/em-format/Makefile.am +++ b/em-format/Makefile.am @@ -34,11 +34,11 @@ evolution_mail_formatter_include_HEADERS = \ e-mail-parser.h \ e-mail-part.h \ e-mail-part-attachment.h \ - e-mail-part-attachment-bar.h \ e-mail-part-audio.h \ e-mail-part-headers.h \ e-mail-part-image.h \ e-mail-part-list.h \ + e-mail-part-secure-button.h \ e-mail-part-utils.h \ e-mail-stripsig-filter.h @@ -69,7 +69,6 @@ libevolution_mail_formatter_la_SOURCES = \ e-mail-formatter-quote.c \ e-mail-formatter-utils.c \ e-mail-formatter-attachment.c \ - e-mail-formatter-attachment-bar.c \ e-mail-formatter-audio.c \ e-mail-formatter-enumtypes.c \ e-mail-formatter-error.c \ @@ -83,7 +82,6 @@ libevolution_mail_formatter_la_SOURCES = \ e-mail-formatter-text-html.c \ e-mail-formatter-text-plain.c \ e-mail-formatter-print-headers.c \ - e-mail-formatter-quote-attachment.c \ e-mail-formatter-quote-headers.c \ e-mail-formatter-quote-message-rfc822.c \ e-mail-formatter-quote-text-enriched.c \ @@ -92,7 +90,6 @@ libevolution_mail_formatter_la_SOURCES = \ e-mail-parser-extension.c \ e-mail-parser.c \ e-mail-parser-application-mbox.c \ - e-mail-parser-attachment-bar.c \ e-mail-parser-audio.c \ e-mail-parser-headers.c \ e-mail-parser-image.c \ @@ -116,11 +113,11 @@ libevolution_mail_formatter_la_SOURCES = \ e-mail-parser-text-plain.c \ e-mail-part.c \ e-mail-part-attachment.c \ - e-mail-part-attachment-bar.c \ e-mail-part-audio.c \ e-mail-part-headers.c \ e-mail-part-image.c \ e-mail-part-list.c \ + e-mail-part-secure-button.c \ e-mail-part-utils.c \ e-mail-stripsig-filter.c \ $(SMIME_EXTENSIONS) diff --git a/em-format/e-mail-formatter-attachment-bar.c b/em-format/e-mail-formatter-attachment-bar.c deleted file mode 100644 index f32cf80..0000000 --- a/em-format/e-mail-formatter-attachment-bar.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * e-mail-formatter-attachment-bar.c - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "e-mail-part-attachment-bar.h" - -#include <glib/gi18n-lib.h> - -#include "e-mail-formatter-extension.h" - -typedef EMailFormatterExtension EMailFormatterAttachmentBar; -typedef EMailFormatterExtensionClass EMailFormatterAttachmentBarClass; - -GType e_mail_formatter_attachment_bar_get_type (void); - -G_DEFINE_TYPE ( - EMailFormatterAttachmentBar, - e_mail_formatter_attachment_bar, - E_TYPE_MAIL_FORMATTER_EXTENSION) - -static const gchar *formatter_mime_types[] = { - "application/vnd.evolution.widget.attachment-bar", - NULL -}; - -static gboolean -emfe_attachment_bar_format (EMailFormatterExtension *extension, - EMailFormatter *formatter, - EMailFormatterContext *context, - EMailPart *part, - GOutputStream *stream, - GCancellable *cancellable) -{ - gchar *str; - - if ((context->mode != E_MAIL_FORMATTER_MODE_NORMAL) && - (context->mode != E_MAIL_FORMATTER_MODE_RAW) && - (context->mode != E_MAIL_FORMATTER_MODE_ALL_HEADERS)) - return FALSE; - - str = g_strdup_printf ( - "<object type=\"application/vnd.evolution.widget.attachment-bar\" " - "height=\"0\" width=\"100%%\" data=\"%s\" id=\"%s\"></object>", - e_mail_part_get_id (part), - e_mail_part_get_id (part)); - - g_output_stream_write_all ( - stream, str, strlen (str), NULL, cancellable, NULL); - - g_free (str); - - return TRUE; -} - -static GtkWidget * -emfe_attachment_bar_get_widget (EMailFormatterExtension *extension, - EMailPartList *context, - EMailPart *part, - GHashTable *params) -{ - EAttachmentStore *store; - GtkWidget *widget; - - g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT_BAR (part), NULL); - - store = e_mail_part_attachment_bar_get_store ( - E_MAIL_PART_ATTACHMENT_BAR (part)); - - widget = e_attachment_bar_new (store); - g_object_set_data (G_OBJECT (store), "attachment-bar", widget); - - return widget; -} - -static void -e_mail_formatter_attachment_bar_class_init (EMailFormatterExtensionClass *class) -{ - class->mime_types = formatter_mime_types; - class->priority = G_PRIORITY_LOW; - class->format = emfe_attachment_bar_format; - class->get_widget = emfe_attachment_bar_get_widget; -} - -static void -e_mail_formatter_attachment_bar_init (EMailFormatterExtension *extension) -{ -} diff --git a/em-format/e-mail-formatter-attachment.c b/em-format/e-mail-formatter-attachment.c index 2a2f2c9..86a39ef 100644 --- a/em-format/e-mail-formatter-attachment.c +++ b/em-format/e-mail-formatter-attachment.c @@ -26,7 +26,6 @@ #include "e-mail-formatter-extension.h" #include "e-mail-inline-filter.h" -#include "e-mail-part-attachment-bar.h" #include "e-mail-part-attachment.h" #include "e-mail-part-utils.h" @@ -44,70 +43,10 @@ G_DEFINE_TYPE ( static const gchar *formatter_mime_types[] = { E_MAIL_PART_ATTACHMENT_MIME_TYPE, - "application/vnd.evolution.widget.attachment-button", + "application/vnd.evolution.attachment-button", NULL }; -static EAttachmentStore * -find_attachment_store (EMailPartList *part_list, - EMailPart *start) -{ - EAttachmentStore *store = NULL; - GQueue queue = G_QUEUE_INIT; - GList *head, *link; - const gchar *start_id; - gchar *tmp, *pos; - EMailPart *part; - gchar *id; - - start_id = e_mail_part_get_id (start); - - e_mail_part_list_queue_parts (part_list, NULL, &queue); - - head = g_queue_peek_head_link (&queue); - - id = g_strconcat (start_id, ".attachment-bar", NULL); - tmp = g_strdup (id); - part = NULL; - do { - d (printf ("Looking up attachment bar as %s\n", id)); - - for (link = head; link != NULL; link = g_list_next (link)) { - EMailPart *p = link->data; - const gchar *p_id; - - p_id = e_mail_part_get_id (p); - - if (g_strcmp0 (p_id, id) == 0) { - part = p; - break; - } - } - - pos = g_strrstr (tmp, "."); - if (!pos) - break; - - g_free (id); - g_free (tmp); - tmp = g_strndup (start_id, pos - tmp); - id = g_strdup_printf ("%s.attachment-bar", tmp); - - } while (pos && !part); - - g_free (id); - g_free (tmp); - - if (part != NULL) - store = e_mail_part_attachment_bar_get_store ( - E_MAIL_PART_ATTACHMENT_BAR (part)); - - while (!g_queue_is_empty (&queue)) - g_object_unref (g_queue_pop_head (&queue)); - - return store; -} - static gboolean emfe_attachment_format (EMailFormatterExtension *extension, EMailFormatter *formatter, @@ -118,13 +57,16 @@ emfe_attachment_format (EMailFormatterExtension *extension, { gchar *text, *html; gchar *button_id; - EAttachmentStore *store; EMailExtensionRegistry *registry; GQueue *extensions; EMailPartAttachment *empa; CamelMimePart *mime_part; CamelMimeFilterToHTMLFlags flags; + GOutputStream *content_stream = NULL; GString *buffer; + gint icon_width, icon_height; + gchar *icon_uri; + gpointer attachment_ptr = NULL; const gchar *attachment_part_id; const gchar *part_id; @@ -139,8 +81,8 @@ emfe_attachment_format (EMailFormatterExtension *extension, EAttachment *attachment; GList *head, *link; - attachment = e_mail_part_attachment_ref_attachment ( - E_MAIL_PART_ATTACHMENT (part)); + attachment = e_mail_part_attachment_ref_attachment (E_MAIL_PART_ATTACHMENT (part)); + attachment_ptr = attachment; head = g_queue_peek_head_link (&part->validities); @@ -161,17 +103,9 @@ emfe_attachment_format (EMailFormatterExtension *extension, pair->validity->encrypt.status); } - store = find_attachment_store (context->part_list, part); - if (store) { - GList *attachments = e_attachment_store_get_attachments (store); - if (!g_list_find (attachments, attachment)) { - e_attachment_store_add_attachment ( - store, attachment); - } - g_list_free_full (attachments, g_object_unref); - } else { - g_warning ("Failed to locate attachment-bar for %s", part_id); - } + e_attachment_set_initially_shown (attachment, e_mail_part_should_show_inline (part)); + + e_mail_formatter_claim_attachment (formatter, attachment); g_object_unref (attachment); } @@ -255,43 +189,29 @@ emfe_attachment_format (EMailFormatterExtension *extension, g_free (text); g_object_unref (mime_part); - if (empa->attachment_view_part_id) - attachment_part_id = empa->attachment_view_part_id; + if (empa->part_id_with_attachment) + attachment_part_id = empa->part_id_with_attachment; else attachment_part_id = part_id; button_id = g_strconcat (attachment_part_id, ".attachment_button", NULL); - /* XXX Wild guess at the initial size. */ - buffer = g_string_sized_new (8192); - - g_string_append_printf ( - buffer, - "<div class=\"attachment\">" - "<table width=\"100%%\" border=\"0\">" - "<tr valign=\"middle\">" - "<td align=\"left\" width=\"100\">" - "<object type=\"application/vnd.evolution.widget.attachment-button\" " - "height=\"20\" width=\"100\" data=\"%s\" id=\"%s\"></object>" - "</td>" - "<td align=\"left\">%s</td>" - "</tr>", part_id, button_id, html); - - g_free (button_id); - g_free (html); + if (!gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &icon_width, &icon_height)) { + icon_width = 16; + icon_height = 16; + } if (extensions != NULL) { - GOutputStream *content_stream; gboolean success = FALSE; content_stream = g_memory_output_stream_new_resizable (); - if (empa->attachment_view_part_id != NULL) { + if (empa->part_id_with_attachment != NULL) { EMailPart *attachment_view_part; attachment_view_part = e_mail_part_list_ref_part ( context->part_list, - empa->attachment_view_part_id); + empa->part_id_with_attachment); /* Avoid recursion. */ if (attachment_view_part == part) @@ -322,51 +242,85 @@ emfe_attachment_format (EMailFormatterExtension *extension, } } - if (success) { - gchar *wrapper_element_id; - gconstpointer data; - gsize size; + e_mail_part_attachment_set_expandable (empa, success); + } - wrapper_element_id = g_strconcat ( - attachment_part_id, ".wrapper", NULL); + icon_uri = e_mail_part_build_uri ( + e_mail_part_list_get_folder (context->part_list), + e_mail_part_list_get_message_uid (context->part_list), + "part_id", G_TYPE_STRING, part_id, + "attachment_icon", G_TYPE_POINTER, attachment_ptr, + "size", G_TYPE_INT, icon_width, + NULL); - data = g_memory_output_stream_get_data ( - G_MEMORY_OUTPUT_STREAM (content_stream)); - size = g_memory_output_stream_get_data_size ( - G_MEMORY_OUTPUT_STREAM (content_stream)); + /* XXX Wild guess at the initial size. */ + buffer = g_string_sized_new (8192); - g_string_append_printf ( - buffer, - "<tr><td colspan=\"2\">" - "<div class=\"attachment-wrapper\" id=\"%s\"", - wrapper_element_id); + g_string_append_printf ( + buffer, + "<div class=\"attachment\">" + "<table width=\"100%%\" border=\"0\">" + "<tr valign=\"middle\">" + "<td align=\"left\" width=\"1px\" style=\"white-space:pre;\">" + "<button type=\"button\" class=\"attachment-expander\" id=\"%s\" value=\"%p\" data=\"%s\" style=\"vertical-align:middle; margin:0px;\" %s>" + "<img id=\"attachment-expander-img-%p\" src=\"gtk-stock://%s?size=%d\" width=\"%dpx\" height=\"%dpx\" style=\"vertical-align:middle;\">" + "<img src=\"%s\" width=\"%dpx\" height=\"%dpx\" style=\"vertical-align:middle;\">" + "</button>" + "<button type=\"button\" class=\"attachment-menu\" id=\"%s\" value=\"%p\" style=\"vertical-align:middle; margin:0px;\">" + "<img src=\"gtk-stock://x-evolution-arrow-down?size=%d\" width=\"%dpx\" height=\"%dpx\" style=\"vertical-align:middle;\">" + "</button>" + "</td><td align=\"left\">%s</td></tr>", + part_id, attachment_ptr, html, e_mail_part_attachment_get_expandable (empa) ? "" : "disabled", + attachment_ptr, e_mail_part_should_show_inline (part) ? "go-down" : "go-next", GTK_ICON_SIZE_BUTTON, icon_width, icon_height, + icon_uri, icon_width, icon_height, + part_id, attachment_ptr, GTK_ICON_SIZE_BUTTON, icon_width, icon_height, + html); + + g_free (icon_uri); + g_free (button_id); + g_free (html); - if (e_mail_part_should_show_inline (part)) { - g_string_append (buffer, ">"); - g_string_append_len (buffer, data, size); - } else { - gchar *inner_html_data; + if (content_stream && e_mail_part_attachment_get_expandable (empa)) { + gchar *wrapper_element_id; + gconstpointer data; + gsize size; - inner_html_data = g_markup_escape_text (data, size); + wrapper_element_id = g_strdup_printf ("attachment-wrapper-%p", attachment_ptr); - g_string_append_printf ( - buffer, - " inner-html-data=\"%s\">", - inner_html_data); + data = g_memory_output_stream_get_data ( + G_MEMORY_OUTPUT_STREAM (content_stream)); + size = g_memory_output_stream_get_data_size ( + G_MEMORY_OUTPUT_STREAM (content_stream)); - g_free (inner_html_data); - } + g_string_append_printf ( + buffer, + "<tr><td colspan=\"2\">" + "<div class=\"attachment-wrapper\" id=\"%s\"", + wrapper_element_id); - g_string_append (buffer, "</div></td></tr>"); + if (e_mail_part_should_show_inline (part)) { + g_string_append (buffer, ">"); + g_string_append_len (buffer, data, size); + } else { + gchar *inner_html_data; - e_mail_part_attachment_set_expandable (empa, TRUE); + inner_html_data = g_markup_escape_text (data, size); - g_free (wrapper_element_id); + g_string_append_printf ( + buffer, + " inner-html-data=\"%s\">", + inner_html_data); + + g_free (inner_html_data); } - g_object_unref (content_stream); + g_string_append (buffer, "</div></td></tr>"); + + g_free (wrapper_element_id); } + g_clear_object (&content_stream); + g_string_append (buffer, "</table></div>"); g_output_stream_write_all ( @@ -377,47 +331,6 @@ emfe_attachment_format (EMailFormatterExtension *extension, return TRUE; } -static GtkWidget * -emfe_attachment_get_widget (EMailFormatterExtension *extension, - EMailPartList *context, - EMailPart *part, - GHashTable *params) -{ - EAttachment *attachment; - EAttachmentStore *store; - EAttachmentView *view; - GtkWidget *widget; - const gchar *part_id; - - g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT (part), NULL); - - attachment = e_mail_part_attachment_ref_attachment ( - E_MAIL_PART_ATTACHMENT (part)); - - part_id = e_mail_part_get_id (part); - - store = find_attachment_store (context, part); - widget = e_attachment_button_new (); - g_object_set_data_full ( - G_OBJECT (widget), - "uri", g_strdup (part_id), - (GDestroyNotify) g_free); - e_attachment_button_set_attachment ( - E_ATTACHMENT_BUTTON (widget), attachment); - - view = g_object_get_data (G_OBJECT (store), "attachment-bar"); - if (view != NULL) - e_attachment_button_set_view ( - E_ATTACHMENT_BUTTON (widget), view); - - gtk_widget_set_can_focus (widget, TRUE); - gtk_widget_show (widget); - - g_object_unref (attachment); - - return widget; -} - static void e_mail_formatter_attachment_class_init (EMailFormatterExtensionClass *class) { @@ -426,7 +339,6 @@ e_mail_formatter_attachment_class_init (EMailFormatterExtensionClass *class) class->mime_types = formatter_mime_types; class->priority = G_PRIORITY_LOW; class->format = emfe_attachment_format; - class->get_widget = emfe_attachment_get_widget; } static void diff --git a/em-format/e-mail-formatter-audio.c b/em-format/e-mail-formatter-audio.c index cf980f8..2837000 100644 --- a/em-format/e-mail-formatter-audio.c +++ b/em-format/e-mail-formatter-audio.c @@ -40,7 +40,7 @@ G_DEFINE_TYPE ( E_TYPE_MAIL_FORMATTER_EXTENSION) static const gchar *formatter_mime_types[] = { - "application/vnd.evolution.widget.audio", + "application/vnd.evolution.audio", "audio/ac3", "audio/x-ac3", "audio/basic", diff --git a/em-format/e-mail-formatter-enums.h b/em-format/e-mail-formatter-enums.h index e7b1868..de64568 100644 --- a/em-format/e-mail-formatter-enums.h +++ b/em-format/e-mail-formatter-enums.h @@ -49,7 +49,6 @@ typedef enum { E_MAIL_FORMATTER_MODE_NORMAL = 0, E_MAIL_FORMATTER_MODE_SOURCE, E_MAIL_FORMATTER_MODE_RAW, - E_MAIL_FORMATTER_MODE_CID, E_MAIL_FORMATTER_MODE_PRINTING, E_MAIL_FORMATTER_MODE_ALL_HEADERS } EMailFormatterMode; diff --git a/em-format/e-mail-formatter-extension.c b/em-format/e-mail-formatter-extension.c index 55b481b..3a2dd52 100644 --- a/em-format/e-mail-formatter-extension.c +++ b/em-format/e-mail-formatter-extension.c @@ -78,64 +78,3 @@ e_mail_formatter_extension_format (EMailFormatterExtension *extension, return class->format ( extension, formatter, context, part, stream, cancellable); } - -/** - * e_mail_formatter_extension_has_widget: - * @extension: an #EMailFormatterExtension - * - * Returns whether the extension can provide a GtkWidget. - * - * Returns: Returns %TRUE when @extension reimplements get_widget(), %FALSE - * otherwise. - */ -gboolean -e_mail_formatter_extension_has_widget (EMailFormatterExtension *extension) -{ - EMailFormatterExtensionClass *class; - - g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), FALSE); - - class = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension); - - return (class->get_widget != NULL); -} - -/** - * e_mail_formatter_extension_get_widget: - * @extension: an #EMailFormatterExtension - * @part: an #EMailPart - * @params: a #GHashTable - * - * A virtual function reimplemented in some mail formatter extensions. The - * function should construct a #GtkWidget for given @part. The @params hash - * table can contain additional parameters listed in the <object> HTML - * element that has requested the widget. - * - * When @bind_dom_func is not %NULL, the callee will set a callback function - * which should be called when the webpage is completely rendered to setup - * bindings between DOM events and the widget. - * - * Returns: Returns a #GtkWidget or %NULL, when error occurs or given - * @extension does not reimplement this method. - */ -GtkWidget * -e_mail_formatter_extension_get_widget (EMailFormatterExtension *extension, - EMailPartList *context, - EMailPart *part, - GHashTable *params) -{ - EMailFormatterExtensionClass *class; - GtkWidget *widget = NULL; - - g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), NULL); - g_return_val_if_fail (part != NULL, NULL); - g_return_val_if_fail (params != NULL, NULL); - - class = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension); - - if (class->get_widget != NULL) - widget = class->get_widget (extension, context, part, params); - - return widget; -} - diff --git a/em-format/e-mail-formatter-extension.h b/em-format/e-mail-formatter-extension.h index adfe98f..8e46deb 100644 --- a/em-format/e-mail-formatter-extension.h +++ b/em-format/e-mail-formatter-extension.h @@ -83,10 +83,6 @@ struct _EMailFormatterExtensionClass { EMailPart *part, GOutputStream *stream, GCancellable *cancellable); - GtkWidget * (*get_widget) (EMailFormatterExtension *extension, - EMailPartList *context, - EMailPart *part, - GHashTable *params); }; GType e_mail_formatter_extension_get_type @@ -98,13 +94,6 @@ gboolean e_mail_formatter_extension_format EMailPart *part, GOutputStream *stream, GCancellable *cancellable); -gboolean e_mail_formatter_extension_has_widget - (EMailFormatterExtension *extension); -GtkWidget * e_mail_formatter_extension_get_widget - (EMailFormatterExtension *extension, - EMailPartList *context, - EMailPart *part, - GHashTable *params); G_END_DECLS diff --git a/em-format/e-mail-formatter-headers.c b/em-format/e-mail-formatter-headers.c index 42d2e9e..2affbe1 100644 --- a/em-format/e-mail-formatter-headers.c +++ b/em-format/e-mail-formatter-headers.c @@ -522,7 +522,7 @@ emfe_headers_format (EMailFormatterExtension *extension, g_string_append_printf ( buffer, "%s id=\"%s\"><table class=\"-e-mail-formatter-header-color\" border=\"0\" width=\"100%%\" " - "style=\"direction: %s\">" + "style=\"direction: %s; border-spacing: 0px\">" "<tr>", (context->mode != E_MAIL_FORMATTER_MODE_PRINTING) ? "<div class=\"headers -e-mail-formatter-body-color\"" : @@ -533,7 +533,7 @@ emfe_headers_format (EMailFormatterExtension *extension, if (is_collapsable) g_string_append_printf ( buffer, - "<td valign=\"top\" width=\"16\">" + "<td valign=\"top\" width=\"16\" style=\"padding-left: 0px\">" "<img src=\"evo-file://%s/%s\" class=\"navigable\" " " id=\"__evo-collapse-headers-img\" />" "</td>", diff --git a/em-format/e-mail-formatter-message-rfc822.c b/em-format/e-mail-formatter-message-rfc822.c index 8884cbc..7f8d6ef 100644 --- a/em-format/e-mail-formatter-message-rfc822.c +++ b/em-format/e-mail-formatter-message-rfc822.c @@ -157,10 +157,6 @@ emfe_message_rfc822_format (EMailFormatterExtension *extension, EMailPart *p = link->data; const gchar *p_id; - /* Skip attachment bar */ - if (e_mail_part_id_has_suffix (part, ".attachment-bar")) - continue; - p_id = e_mail_part_get_id (p); /* Check for nested rfc822 messages */ diff --git a/em-format/e-mail-formatter-quote-attachment.c b/em-format/e-mail-formatter-quote-attachment.c deleted file mode 100644 index dcc5300..0000000 --- a/em-format/e-mail-formatter-quote-attachment.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * e-mail-formatter-qoute-attachment.c - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <glib/gi18n-lib.h> - -#include <camel/camel.h> - -#include <e-util/e-util.h> - -#include "e-mail-formatter-quote.h" -#include "e-mail-part-attachment.h" -#include "e-mail-part-utils.h" - -#define d(x) - -typedef EMailFormatterExtension EMailFormatterQuoteAttachment; -typedef EMailFormatterExtensionClass EMailFormatterQuoteAttachmentClass; - -GType e_mail_formatter_quote_attachment_get_type (void); - -G_DEFINE_TYPE ( - EMailFormatterQuoteAttachment, - e_mail_formatter_quote_attachment, - E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION) - -static const gchar *formatter_mime_types[] = { - E_MAIL_PART_ATTACHMENT_MIME_TYPE, - NULL -}; - -static gboolean -emfqe_attachment_format (EMailFormatterExtension *extension, - EMailFormatter *formatter, - EMailFormatterContext *context, - EMailPart *part, - GOutputStream *stream, - GCancellable *cancellable) -{ - gchar *text, *html; - EMailPartAttachment *empa; - EMailPart *attachment_view_part; - CamelMimeFilterToHTMLFlags text_format_flags; - CamelMimePart *mime_part; - const gchar *string; - - empa = E_MAIL_PART_ATTACHMENT (part); - - if (!empa || !empa->attachment_view_part_id) - return FALSE; - - attachment_view_part = e_mail_part_list_ref_part ( - context->part_list, empa->attachment_view_part_id); - if (attachment_view_part == NULL) - return FALSE; - - string = "<br><br>"; - g_output_stream_write_all ( - stream, string, strlen (string), NULL, cancellable, NULL); - - text_format_flags = - e_mail_formatter_get_text_format_flags (formatter); - mime_part = e_mail_part_ref_mime_part (part); - text = e_mail_part_describe ( - mime_part, - empa->snoop_mime_type && *empa->snoop_mime_type ? - empa->snoop_mime_type : - e_mail_part_get_mime_type (part)); - g_object_unref (mime_part); - - html = camel_text_to_html ( - text, - text_format_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, - 0); - g_output_stream_write_all ( - stream, html, strlen (html), NULL, cancellable, NULL); - g_output_stream_write_all ( - stream, "<br>", 4, NULL, cancellable, NULL); - g_free (html); - g_free (text); - - string = "<blockquote type=cite>\n"; - g_output_stream_write_all ( - stream, string, strlen (string), NULL, cancellable, NULL); - - e_mail_formatter_format_as ( - formatter, context, attachment_view_part, - stream, NULL, cancellable); - - string = "</blockquote>"; - g_output_stream_write_all ( - stream, string, strlen (string), NULL, cancellable, NULL); - - g_object_unref (attachment_view_part); - - return TRUE; -} - -static void -e_mail_formatter_quote_attachment_class_init (EMailFormatterExtensionClass *class) -{ - class->mime_types = formatter_mime_types; - class->priority = G_PRIORITY_HIGH; - class->format = emfqe_attachment_format; -} - -static void -e_mail_formatter_quote_attachment_init (EMailFormatterExtension *extension) -{ -} diff --git a/em-format/e-mail-formatter-quote-message-rfc822.c b/em-format/e-mail-formatter-quote-message-rfc822.c index 180a283..7ed0bd4 100644 --- a/em-format/e-mail-formatter-quote-message-rfc822.c +++ b/em-format/e-mail-formatter-quote-message-rfc822.c @@ -89,10 +89,6 @@ emfqe_message_rfc822_format (EMailFormatterExtension *extension, p_id = e_mail_part_get_id (p); - /* Skip attachment bar */ - if (e_mail_part_id_has_suffix (p, ".attachment-bar")) - continue; - if (e_mail_part_id_has_suffix (p, ".headers.")) { if (qc->qf_flags & E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS) { e_mail_formatter_format_as ( diff --git a/em-format/e-mail-formatter-quote.c b/em-format/e-mail-formatter-quote.c index adbe223..24abcbb 100644 --- a/em-format/e-mail-formatter-quote.c +++ b/em-format/e-mail-formatter-quote.c @@ -38,7 +38,6 @@ struct _EMailFormatterQuotePrivate { }; /* internal formatter extensions */ -GType e_mail_formatter_quote_attachment_get_type (void); GType e_mail_formatter_quote_headers_get_type (void); GType e_mail_formatter_quote_message_rfc822_get_type (void); GType e_mail_formatter_quote_text_enriched_get_type (void); @@ -161,7 +160,6 @@ static void e_mail_formatter_quote_base_init (EMailFormatterQuoteClass *class) { /* Register internal extensions. */ - g_type_ensure (e_mail_formatter_quote_attachment_get_type ()); g_type_ensure (e_mail_formatter_quote_headers_get_type ()); g_type_ensure (e_mail_formatter_quote_message_rfc822_get_type ()); g_type_ensure (e_mail_formatter_quote_text_enriched_get_type ()); diff --git a/em-format/e-mail-formatter-secure-button.c b/em-format/e-mail-formatter-secure-button.c index d4f6a96..80cfb09 100644 --- a/em-format/e-mail-formatter-secure-button.c +++ b/em-format/e-mail-formatter-secure-button.c @@ -23,11 +23,6 @@ #include <e-util/e-util.h> -#if defined (HAVE_NSS) && defined (ENABLE_SMIME) -#include "certificate-manager.h" -#include "e-cert-db.h" -#endif - #include "e-mail-formatter-extension.h" typedef EMailFormatterExtension EMailFormatterSecureButton; @@ -41,7 +36,7 @@ G_DEFINE_TYPE ( E_TYPE_MAIL_FORMATTER_EXTENSION) static const gchar *formatter_mime_types[] = { - "application/vnd.evolution.widget.secure-button", + "application/vnd.evolution.secure-button", NULL }; @@ -76,139 +71,25 @@ static const GdkRGBA smime_sign_colour[6] = { { 0.0, 0.0, 0.0, 1.0 } }; -static gboolean -emfe_secure_button_format (EMailFormatterExtension *extension, - EMailFormatter *formatter, - EMailFormatterContext *context, - EMailPart *part, - GOutputStream *stream, - GCancellable *cancellable) -{ - gchar *str; +/* This is awkward, but there is no header file for it. On the other hand, + the functions are meant private, where they really are, being defined this way. */ +const gchar *e_mail_formatter_secure_button_get_encrypt_description (CamelCipherValidityEncrypt status); +const gchar *e_mail_formatter_secure_button_get_sign_description (CamelCipherValiditySign status); - if ((context->mode != E_MAIL_FORMATTER_MODE_NORMAL) && - (context->mode != E_MAIL_FORMATTER_MODE_RAW) && - (context->mode != E_MAIL_FORMATTER_MODE_ALL_HEADERS)) - return FALSE; - - str = g_strdup_printf ( - "<object type=\"application/vnd.evolution.widget.secure-button\" " - "height=\"20\" width=\"100%%\" data=\"%s\" id=\"%s\"></object>", - e_mail_part_get_id (part), - e_mail_part_get_id (part)); - - g_output_stream_write_all ( - stream, str, strlen (str), NULL, cancellable, NULL); - - g_free (str); - - return TRUE; -} - -#if defined (HAVE_NSS) && defined (ENABLE_SMIME) -static void -viewcert_clicked (GtkWidget *button, - GtkWidget *grid) +const gchar * +e_mail_formatter_secure_button_get_sign_description (CamelCipherValiditySign status) { - CamelCipherCertInfo *info = g_object_get_data ((GObject *) button, "e-cert-info"); - ECert *ec = NULL; - - if (info->cert_data) - ec = e_cert_new (CERT_DupCertificate (info->cert_data)); + g_return_val_if_fail (status >= 0 && status < G_N_ELEMENTS (smime_sign_table), NULL); - if (ec != NULL) { - GtkWidget *dialog, *parent; - - parent = gtk_widget_get_toplevel (grid); - if (!parent || !GTK_IS_WINDOW (parent)) - parent = NULL; - - dialog = e_cert_manager_new_certificate_viewer ((GtkWindow *) parent, ec); - - gtk_widget_show (dialog); - g_signal_connect ( - dialog, "response", - G_CALLBACK (gtk_widget_destroy), NULL); - - g_object_unref (ec); - } else { - g_warning ( - "can't find certificate for %s <%s>", - info->name ? info->name : "", - info->email ? info->email : ""); - } + return _(smime_sign_table[status].description); } -#endif -static void -info_response (GtkWidget *widget, - guint button, - gpointer user_data) -{ - gtk_widget_destroy (widget); -} - -static void -add_cert_table (GtkWidget *grid, - GQueue *certlist, - gpointer user_data) +const gchar * +e_mail_formatter_secure_button_get_encrypt_description (CamelCipherValidityEncrypt status) { - GList *head, *link; - GtkTable *table; - gint n = 0; - - table = (GtkTable *) gtk_table_new (certlist->length, 2, FALSE); - - head = g_queue_peek_head_link (certlist); - - for (link = head; link != NULL; link = g_list_next (link)) { - CamelCipherCertInfo *info = link->data; - gchar *la = NULL; - const gchar *l = NULL; - - if (info->name) { - if (info->email && strcmp (info->name, info->email) != 0) - l = la = g_strdup_printf ("%s <%s>", info->name, info->email); - else - l = info->name; - } else { - if (info->email) - l = info->email; - } - - if (l) { - GtkWidget *w; -#if defined (HAVE_NSS) && defined (ENABLE_SMIME) - ECert *ec = NULL; -#endif - w = gtk_label_new (l); - gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); - g_free (la); - gtk_table_attach (table, w, 0, 1, n, n + 1, GTK_FILL, GTK_FILL, 3, 3); -#if defined (HAVE_NSS) && defined (ENABLE_SMIME) - w = gtk_button_new_with_mnemonic (_("_View Certificate")); - gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3); - g_object_set_data ((GObject *) w, "e-cert-info", info); - g_signal_connect ( - w, "clicked", - G_CALLBACK (viewcert_clicked), grid); - - if (info->cert_data) - ec = e_cert_new (CERT_DupCertificate (info->cert_data)); - - if (ec == NULL) - gtk_widget_set_sensitive (w, FALSE); - else - g_object_unref (ec); -#else - w = gtk_label_new (_("This certificate is not viewable")); - gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3); -#endif - n++; - } - } + g_return_val_if_fail (status >= 0 && status < G_N_ELEMENTS (smime_encrypt_table), NULL); - gtk_container_add (GTK_CONTAINER (grid), GTK_WIDGET (table)); + return _(smime_encrypt_table[status].description); } static void @@ -246,9 +127,9 @@ format_cert_infos (GQueue *cert_infos, g_string_append (output_buffer, cinfo->name); if (cinfo->email != NULL && *cinfo->email != '\0') { - g_string_append (output_buffer, " <"); + g_string_append (output_buffer, " <"); g_string_append (output_buffer, cinfo->email); - g_string_append (output_buffer, ">"); + g_string_append (output_buffer, ">"); } } else if (cinfo->email != NULL && *cinfo->email != '\0') { @@ -263,130 +144,22 @@ format_cert_infos (GQueue *cert_infos, } static void -secure_button_clicked_cb (GtkWidget *widget, - CamelCipherValidity *validity) -{ - GtkBuilder *builder; - GtkWidget *grid, *w; - GtkWidget *dialog; - - g_return_if_fail (validity != NULL); - - /* Make sure our custom widget classes are registered with - * GType before we load the GtkBuilder definition file. */ - g_type_ensure (E_TYPE_DATE_EDIT); - - builder = gtk_builder_new (); - e_load_ui_builder_definition (builder, "mail-dialogs.ui"); - - dialog = e_builder_get_widget (builder, "message_security_dialog"); - - grid = e_builder_get_widget (builder, "signature_grid"); - w = gtk_label_new (_(smime_sign_table[validity->sign.status].description)); - gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); - gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); - gtk_label_set_max_width_chars (GTK_LABEL (w), 80); - gtk_container_add (GTK_CONTAINER (grid), w); - if (validity->sign.description) { - GtkTextBuffer *buffer; - - buffer = gtk_text_buffer_new (NULL); - gtk_text_buffer_set_text ( - buffer, validity->sign.description, - strlen (validity->sign.description)); - w = g_object_new ( - gtk_scrolled_window_get_type (), - "hscrollbar_policy", GTK_POLICY_AUTOMATIC, - "vscrollbar_policy", GTK_POLICY_AUTOMATIC, - "shadow_type", GTK_SHADOW_IN, - "expand", TRUE, - "child", g_object_new (gtk_text_view_get_type (), - "buffer", buffer, - "cursor_visible", FALSE, - "editable", FALSE, - NULL), - "width_request", 500, - "height_request", 80, - NULL); - g_object_unref (buffer); - - gtk_container_add (GTK_CONTAINER (grid), w); - } - - if (!g_queue_is_empty (&validity->sign.signers)) - add_cert_table ( - grid, &validity->sign.signers, NULL); - - gtk_widget_show_all (grid); - - grid = e_builder_get_widget (builder, "encryption_grid"); - w = gtk_label_new (_(smime_encrypt_table[validity->encrypt.status].description)); - gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); - gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); - gtk_label_set_max_width_chars (GTK_LABEL (w), 80); - gtk_container_add (GTK_CONTAINER (grid), w); - if (validity->encrypt.description) { - GtkTextBuffer *buffer; - - buffer = gtk_text_buffer_new (NULL); - gtk_text_buffer_set_text ( - buffer, validity->encrypt.description, - strlen (validity->encrypt.description)); - w = g_object_new ( - gtk_scrolled_window_get_type (), - "hscrollbar_policy", GTK_POLICY_AUTOMATIC, - "vscrollbar_policy", GTK_POLICY_AUTOMATIC, - "shadow_type", GTK_SHADOW_IN, - "expand", TRUE, - "child", g_object_new (gtk_text_view_get_type (), - "buffer", buffer, - "cursor_visible", FALSE, - "editable", FALSE, - NULL), - "width_request", 500, - "height_request", 80, - NULL); - g_object_unref (buffer); - - gtk_container_add (GTK_CONTAINER (grid), w); - } - - if (!g_queue_is_empty (&validity->encrypt.encrypters)) - add_cert_table (grid, &validity->encrypt.encrypters, NULL); - - gtk_widget_show_all (grid); - - g_object_unref (builder); - - g_signal_connect ( - dialog, "response", - G_CALLBACK (info_response), NULL); - - gtk_widget_show (dialog); -} - -static void add_photo_cb (gpointer data, gpointer user_data) { CamelCipherCertInfo *cert_info = data; gint width, height; - GtkWidget *image; - GdkPixbuf *pixbuf, *scaled; - GtkBox *box = user_data; + GString *html = user_data; const gchar *photo_filename; + gchar *uri; g_return_if_fail (cert_info != NULL); - g_return_if_fail (GTK_IS_BOX (box)); + g_return_if_fail (html != NULL); photo_filename = camel_cipher_certinfo_get_property (cert_info, CAMEL_CIPHER_CERT_INFO_PROPERTY_PHOTO_FILENAME); if (!photo_filename || !g_file_test (photo_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) return; - pixbuf = gdk_pixbuf_new_from_file (photo_filename, NULL); - if (!pixbuf) - return; - if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &width, &height)) { width = 32; height = 32; @@ -397,30 +170,25 @@ add_photo_cb (gpointer data, if (height < 32) height = 32; - scaled = e_icon_factory_pixbuf_scale (pixbuf, width, height); - g_object_unref (pixbuf); + uri = g_filename_to_uri (photo_filename, NULL, NULL); - if (!scaled) - return; - - image = gtk_image_new_from_pixbuf (scaled); - g_object_unref (scaled); + g_string_append_printf (html, "<img src=\"evo-%s\" width=\"%dpx\" height=\"%dpx\" style=\"vertical-align:middle; margin-right:4px;\">", + uri, width, height); - if (!image) - return; - - gtk_box_pack_start (box, image, FALSE, FALSE, 0); + g_free (uri); } -static GtkWidget * -secure_button_get_widget_for_validity (CamelCipherValidity *validity) +static void +secure_button_format_validity (EMailPart *part, + CamelCipherValidity *validity, + GString *html) { - GtkWidget *box, *button, *layout, *widget; const gchar *icon_name; gchar *description; + gint icon_width, icon_height; GString *buffer; - g_return_val_if_fail (validity != NULL, NULL); + g_return_if_fail (validity != NULL); buffer = g_string_new (""); @@ -441,7 +209,7 @@ secure_button_get_widget_for_validity (CamelCipherValidity *validity) gint status; if (validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) - g_string_append (buffer, "\n"); + g_string_append (buffer, "<br>\n"); status = validity->encrypt.status; desc = smime_encrypt_table[status].shortdesc; @@ -451,90 +219,70 @@ secure_button_get_widget_for_validity (CamelCipherValidity *validity) description = g_string_free (buffer, FALSE); /* FIXME: need to have it based on encryption and signing too */ - if (validity->sign.status != 0) + if (validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) icon_name = smime_sign_table[validity->sign.status].icon; else icon_name = smime_encrypt_table[validity->encrypt.status].icon; - box = gtk_event_box_new (); - if (validity->sign.status != 0) - gtk_widget_override_background_color ( - box, GTK_STATE_FLAG_NORMAL, - &smime_sign_colour[validity->sign.status]); - - layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_container_add (GTK_CONTAINER (box), layout); - - button = gtk_button_new (); - gtk_box_pack_start (GTK_BOX (layout), button, FALSE, FALSE, 0); - g_signal_connect ( - button, "clicked", - G_CALLBACK (secure_button_clicked_cb), validity); - - widget = gtk_image_new_from_icon_name ( - icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR); - gtk_button_set_image (GTK_BUTTON (button), widget); - - g_queue_foreach (&validity->sign.signers, add_photo_cb, layout); - g_queue_foreach (&validity->encrypt.encrypters, add_photo_cb, layout); - - widget = gtk_label_new (description); - g_object_set (G_OBJECT (widget), - "wrap", TRUE, - "width-chars", 20, - "max-width-chars", 80, - "xalign", 0.0, - "halign", GTK_ALIGN_FILL, - "hexpand", TRUE, - NULL); - /* make sure the text color doesn't change with theme */ - gtk_widget_override_color (widget, GTK_STATE_FLAG_NORMAL, &smime_sign_colour[5]); - gtk_box_pack_start (GTK_BOX (layout), widget, TRUE, TRUE, 0); + if (!gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &icon_width, &icon_height)) { + icon_width = 24; + icon_height = 24; + } - g_free (description); + g_string_append (html, "<table width=\"100%\" style=\"vertical-align:middle;"); + if (validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE && + smime_sign_colour[validity->sign.status].alpha > 1e-9) + g_string_append_printf (html, " background:#%06x;", + e_rgba_to_value (&smime_sign_colour[validity->sign.status])); + g_string_append (html, "\"><tr>"); + + g_string_append_printf (html, + "<td style=\"width:1px;\"><button type=\"button\" class=\"secure-button\" id=\"secure-button\" value=\"%p:%p\" accesskey=\"\" style=\"vertical-align:middle;\">" + "<img src=\"gtk-stock://%s?size=%d\" width=\"%dpx\" height=\"%dpx\" style=\"vertical-align:middle;\"></button></td><td><span style=\"color:#%06x; vertical-align:middle;\">", + part, validity, icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR, + icon_width, icon_height, e_rgba_to_value (&smime_sign_colour[5])); + + g_queue_foreach (&validity->sign.signers, add_photo_cb, html); + g_queue_foreach (&validity->encrypt.encrypters, add_photo_cb, html); + + g_string_append_printf (html, "%s</span></td></tr></table>\n", description); - return box; + g_free (description); } -static GtkWidget * -emfe_secure_button_get_widget (EMailFormatterExtension *extension, - EMailPartList *context, - EMailPart *part, - GHashTable *params) +static gboolean +emfe_secure_button_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + GOutputStream *stream, + GCancellable *cancellable) { - GtkWidget *grid; GList *head, *link; + GString *html; - g_return_val_if_fail (part != NULL, NULL); - - grid = g_object_new ( - GTK_TYPE_GRID, - "orientation", GTK_ORIENTATION_VERTICAL, - "row-spacing", 2, - "halign", GTK_ALIGN_FILL, - "hexpand", TRUE, - NULL); + if ((context->mode != E_MAIL_FORMATTER_MODE_NORMAL) && + (context->mode != E_MAIL_FORMATTER_MODE_RAW) && + (context->mode != E_MAIL_FORMATTER_MODE_ALL_HEADERS)) + return FALSE; + html = g_string_new (""); head = g_queue_peek_head_link (&part->validities); for (link = head; link != NULL; link = g_list_next (link)) { EMailPartValidityPair *pair = link->data; - GtkWidget *widget; - if (pair == NULL) + if (!pair) continue; - widget = secure_button_get_widget_for_validity (pair->validity); - if (widget != NULL) { - gtk_widget_set_halign (widget, GTK_ALIGN_FILL); - gtk_widget_set_hexpand (widget, TRUE); - gtk_container_add (GTK_CONTAINER (grid), widget); - } + secure_button_format_validity (part, pair->validity, html); } - gtk_widget_show_all (grid); + g_output_stream_write_all (stream, html->str, html->len, NULL, cancellable, NULL); - return grid; + g_string_free (html, TRUE); + + return TRUE; } static void @@ -543,7 +291,6 @@ e_mail_formatter_secure_button_class_init (EMailFormatterExtensionClass *class) class->mime_types = formatter_mime_types; class->priority = G_PRIORITY_LOW; class->format = emfe_secure_button_format; - class->get_widget = emfe_secure_button_get_widget; } static void diff --git a/em-format/e-mail-formatter.c b/em-format/e-mail-formatter.c index 668b2ef..13e68c5 100644 --- a/em-format/e-mail-formatter.c +++ b/em-format/e-mail-formatter.c @@ -34,7 +34,7 @@ #define E_MAIL_FORMATTER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterPrivate))\ + ((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterPrivate)) #define STYLESHEET_URI \ "evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css" @@ -63,7 +63,6 @@ struct _AsyncContext { /* internal formatter extensions */ GType e_mail_formatter_attachment_get_type (void); -GType e_mail_formatter_attachment_bar_get_type (void); GType e_mail_formatter_audio_get_type (void); GType e_mail_formatter_error_get_type (void); GType e_mail_formatter_headers_get_type (void); @@ -96,6 +95,7 @@ enum { enum { NEED_REDRAW, + CLAIM_ATTACHMENT, LAST_SIGNAL }; @@ -451,8 +451,7 @@ mail_formatter_run (EMailFormatter *formatter, if (!ok) { /* We don't want to source these */ - if (e_mail_part_id_has_suffix (part, ".headers") || - e_mail_part_id_has_suffix (part, "attachment-bar")) + if (e_mail_part_id_has_suffix (part, ".headers")) continue; e_mail_formatter_format_as ( @@ -560,7 +559,6 @@ e_mail_formatter_base_init (EMailFormatterClass *class) /* Register internal extensions. */ g_type_ensure (e_mail_formatter_attachment_get_type ()); - g_type_ensure (e_mail_formatter_attachment_bar_get_type ()); g_type_ensure (e_mail_formatter_audio_get_type ()); g_type_ensure (e_mail_formatter_error_get_type ()); g_type_ensure (e_mail_formatter_headers_get_type ()); @@ -773,6 +771,14 @@ e_mail_formatter_class_init (EMailFormatterClass *class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + signals[CLAIM_ATTACHMENT] = g_signal_new ( + "claim-attachment", + E_TYPE_MAIL_FORMATTER, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EMailFormatterClass, claim_attachment), + NULL, NULL, NULL, + G_TYPE_NONE, 1, E_TYPE_ATTACHMENT); + signals[NEED_REDRAW] = g_signal_new ( "need-redraw", E_TYPE_MAIL_FORMATTER, @@ -837,6 +843,16 @@ e_mail_formatter_get_type (void) } void +e_mail_formatter_claim_attachment (EMailFormatter *formatter, + EAttachment *attachment) +{ + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + g_signal_emit (formatter, signals[CLAIM_ATTACHMENT], 0, attachment); +} + +void e_mail_formatter_format_sync (EMailFormatter *formatter, EMailPartList *part_list, GOutputStream *stream, @@ -1117,7 +1133,6 @@ e_mail_formatter_format_text (EMailFormatter *formatter, g_output_stream_flush (stream, cancellable, NULL); g_object_unref (stream); - g_clear_object (&windows); g_clear_object (&mime_part); } @@ -1131,9 +1146,9 @@ e_mail_formatter_get_sub_html_header (EMailFormatter *formatter) "<meta name=\"generator\" content=\"Evolution Mail\"/>\n" "<title>Evolution Mail Display</title>\n" "<link type=\"text/css\" rel=\"stylesheet\" " - " href=\"" STYLESHEET_URI "\"/>\n" + " href=\"" STYLESHEET_URI "\"/>\n" "<style type=\"text/css\">\n" - " table th { font-weight: bold; }\n" + " table th { font-weight: bold; }\n" "</style>\n" "</head>" "<body class=\"-e-web-view-background-color -e-web-view-text-color\">"; @@ -1149,9 +1164,9 @@ e_mail_formatter_get_html_header (EMailFormatter *formatter) "<meta name=\"generator\" content=\"Evolution Mail\"/>\n" "<title>Evolution Mail Display</title>\n" "<link type=\"text/css\" rel=\"stylesheet\" " - " href=\"" STYLESHEET_URI "\"/>\n" + " href=\"" STYLESHEET_URI "\"/>\n" "<style type=\"text/css\">\n" - " table th { font-weight: bold; }\n" + " table th { font-weight: bold; }\n" "</style>\n" "</head>" "<body class=\"-e-mail-formatter-body-color " diff --git a/em-format/e-mail-formatter.h b/em-format/e-mail-formatter.h index a05755c..777e31a 100644 --- a/em-format/e-mail-formatter.h +++ b/em-format/e-mail-formatter.h @@ -85,13 +85,17 @@ struct _EMailFormatterClass { /* Signals */ void (*need_redraw) (EMailFormatter *formatter); + void (*claim_attachment) (EMailFormatter *formatter, + EAttachment *attachment); }; GType e_mail_formatter_get_type (void); EMailFormatter * e_mail_formatter_new (void); - +void e_mail_formatter_claim_attachment + (EMailFormatter *formatter, + EAttachment *attachment); void e_mail_formatter_format_sync (EMailFormatter *formatter, EMailPartList *part_list, GOutputStream *stream, diff --git a/em-format/e-mail-parser-application-smime.c b/em-format/e-mail-parser-application-smime.c index 95f07b4..f3673bf 100644 --- a/em-format/e-mail-parser-application-smime.c +++ b/em-format/e-mail-parser-application-smime.c @@ -120,7 +120,7 @@ empe_app_smime_parse (EMailParserExtension *extension, e_mail_parser_parse_part_as ( parser, part, part_id, - "application/vnd.evolution.widget.secure-button", + "application/vnd.evolution.secure-button", cancellable, &work_queue); mail_part = g_queue_peek_head (&work_queue); diff --git a/em-format/e-mail-parser-attachment-bar.c b/em-format/e-mail-parser-attachment-bar.c deleted file mode 100644 index 8bc7402..0000000 --- a/em-format/e-mail-parser-attachment-bar.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * e-mail-parser-attachment-bar.c - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "e-mail-part-attachment-bar.h" - -#include <glib/gi18n-lib.h> - -#include <e-util/e-util.h> - -#include "e-mail-parser-extension.h" - -typedef EMailParserExtension EMailParserAttachmentBar; -typedef EMailParserExtensionClass EMailParserAttachmentBarClass; - -GType e_mail_parser_attachment_bar_get_type (void); - -G_DEFINE_TYPE ( - EMailParserAttachmentBar, - e_mail_parser_attachment_bar, - E_TYPE_MAIL_PARSER_EXTENSION) - -static const gchar *parser_mime_types[] = { - E_MAIL_PART_ATTACHMENT_BAR_MIME_TYPE, - NULL -}; - -static gboolean -empe_attachment_bar_parse (EMailParserExtension *extension, - EMailParser *parser, - CamelMimePart *part, - GString *part_id, - GCancellable *cancellable, - GQueue *out_mail_parts) -{ - EMailPart *mail_part; - gint len; - - len = part_id->len; - g_string_append (part_id, ".attachment-bar"); - mail_part = e_mail_part_attachment_bar_new (part, part_id->str); - e_mail_part_set_mime_type (mail_part, parser_mime_types[0]); - g_string_truncate (part_id, len); - - g_queue_push_tail (out_mail_parts, mail_part); - - return TRUE; -} - -static void -e_mail_parser_attachment_bar_class_init (EMailParserExtensionClass *class) -{ - class->mime_types = parser_mime_types; - class->priority = G_PRIORITY_LOW; - class->parse = empe_attachment_bar_parse; -} - -static void -e_mail_parser_attachment_bar_init (EMailParserExtension *extension) -{ -} diff --git a/em-format/e-mail-parser-inlinepgp-encrypted.c b/em-format/e-mail-parser-inlinepgp-encrypted.c index 56d5fc5..bdabcb8 100644 --- a/em-format/e-mail-parser-inlinepgp-encrypted.c +++ b/em-format/e-mail-parser-inlinepgp-encrypted.c @@ -148,7 +148,7 @@ empe_inlinepgp_encrypted_parse (EMailParserExtension *extension, e_mail_parser_parse_part_as ( parser, part, part_id, - "application/vnd.evolution.widget.secure-button", + "application/vnd.evolution.secure-button", cancellable, &work_queue); mail_part = g_queue_peek_head (&work_queue); diff --git a/em-format/e-mail-parser-inlinepgp-signed.c b/em-format/e-mail-parser-inlinepgp-signed.c index 9f08583..84f38bd 100644 --- a/em-format/e-mail-parser-inlinepgp-signed.c +++ b/em-format/e-mail-parser-inlinepgp-signed.c @@ -160,7 +160,7 @@ empe_inlinepgp_signed_parse (EMailParserExtension *extension, e_mail_parser_parse_part_as ( parser, part, part_id, - "application/vnd.evolution.widget.secure-button", + "application/vnd.evolution.secure-button", cancellable, &work_queue); mail_part = g_queue_peek_head (&work_queue); diff --git a/em-format/e-mail-parser-message.c b/em-format/e-mail-parser-message.c index 282cef4..0a3b77f 100644 --- a/em-format/e-mail-parser-message.c +++ b/em-format/e-mail-parser-message.c @@ -63,12 +63,6 @@ empe_message_parse (EMailParserExtension *extension, "application/vnd.evolution.headers", cancellable, out_mail_parts); - /* Attachment Bar */ - e_mail_parser_parse_part_as ( - parser, part, part_id, - "application/vnd.evolution.widget.attachment-bar", - cancellable, out_mail_parts); - ct = camel_mime_part_get_content_type (part); mime_type = camel_content_type_simple (ct); diff --git a/em-format/e-mail-parser-multipart-encrypted.c b/em-format/e-mail-parser-multipart-encrypted.c index dff97b3..6c8a65e 100644 --- a/em-format/e-mail-parser-multipart-encrypted.c +++ b/em-format/e-mail-parser-multipart-encrypted.c @@ -143,7 +143,7 @@ empe_mp_encrypted_parse (EMailParserExtension *extension, e_mail_parser_parse_part_as ( parser, part, part_id, - "application/vnd.evolution.widget.secure-button", + "application/vnd.evolution.secure-button", cancellable, &work_queue); mail_part = g_queue_peek_head (&work_queue); diff --git a/em-format/e-mail-parser-multipart-signed.c b/em-format/e-mail-parser-multipart-signed.c index c467ec7..f90d645 100644 --- a/em-format/e-mail-parser-multipart-signed.c +++ b/em-format/e-mail-parser-multipart-signed.c @@ -188,7 +188,7 @@ empe_mp_signed_parse (EMailParserExtension *extension, e_mail_parser_parse_part_as ( parser, part, part_id, - "application/vnd.evolution.widget.secure-button", + "application/vnd.evolution.secure-button", cancellable, &work_queue); mail_part = g_queue_peek_head (&work_queue); diff --git a/em-format/e-mail-parser-secure-button.c b/em-format/e-mail-parser-secure-button.c index 61832a0..e458598 100644 --- a/em-format/e-mail-parser-secure-button.c +++ b/em-format/e-mail-parser-secure-button.c @@ -23,6 +23,7 @@ #include <e-util/e-util.h> +#include "e-mail-part-secure-button.h" #include "e-mail-parser-extension.h" typedef EMailParserExtension EMailParserSecureButton; @@ -36,7 +37,7 @@ G_DEFINE_TYPE ( E_TYPE_MAIL_PARSER_EXTENSION) static const gchar *parser_mime_types[] = { - "application/vnd.evolution.widget.secure-button", + "application/vnd.evolution.secure-button", NULL }; @@ -53,7 +54,7 @@ empe_secure_button_parse (EMailParserExtension *extension, len = part_id->len; g_string_append (part_id, ".secure_button"); - mail_part = e_mail_part_new (part, part_id->str); + mail_part = e_mail_part_secure_button_new (part, part_id->str); e_mail_part_set_mime_type (mail_part, parser_mime_types[0]); g_string_truncate (part_id, len); diff --git a/em-format/e-mail-parser-text-plain.c b/em-format/e-mail-parser-text-plain.c index ccb371d..aa5c55a 100644 --- a/em-format/e-mail-parser-text-plain.c +++ b/em-format/e-mail-parser-text-plain.c @@ -185,7 +185,7 @@ empe_text_plain_parse (EMailParserExtension *extension, empa->shown = FALSE; attachment = e_mail_part_attachment_ref_attachment (empa); - e_attachment_set_shown (attachment, FALSE); + e_attachment_set_initially_shown (attachment, FALSE); e_attachment_set_can_show (attachment, FALSE); att_part = e_attachment_ref_mime_part (attachment); diff --git a/em-format/e-mail-parser.c b/em-format/e-mail-parser.c index c78cb70..b5fe91e 100644 --- a/em-format/e-mail-parser.c +++ b/em-format/e-mail-parser.c @@ -53,7 +53,6 @@ enum { /* internal parser extensions */ GType e_mail_parser_application_mbox_get_type (void); -GType e_mail_parser_attachment_bar_get_type (void); GType e_mail_parser_audio_get_type (void); GType e_mail_parser_headers_get_type (void); GType e_mail_parser_message_get_type (void); @@ -221,7 +220,6 @@ e_mail_parser_base_init (EMailParserClass *class) /* Register internal extensions. */ g_type_ensure (e_mail_parser_application_mbox_get_type ()); - g_type_ensure (e_mail_parser_attachment_bar_get_type ()); g_type_ensure (e_mail_parser_audio_get_type ()); g_type_ensure (e_mail_parser_headers_get_type ()); g_type_ensure (e_mail_parser_message_get_type ()); @@ -714,13 +712,13 @@ e_mail_parser_wrap_as_attachment (EMailParser *parser, first_part = g_queue_peek_head (parts_queue); if (first_part != NULL) { const gchar *id = e_mail_part_get_id (first_part); - empa->attachment_view_part_id = g_strdup (id); + empa->part_id_with_attachment = g_strdup (id); first_part->is_hidden = TRUE; } attachment = e_mail_part_attachment_ref_attachment (empa); - e_attachment_set_shown (attachment, empa->shown); + e_attachment_set_initially_shown (attachment, empa->shown); e_attachment_set_can_show ( attachment, extensions && !g_queue_is_empty (extensions)); diff --git a/em-format/e-mail-part-attachment-bar.c b/em-format/e-mail-part-attachment-bar.c deleted file mode 100644 index dc599f0..0000000 --- a/em-format/e-mail-part-attachment-bar.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * e-mail-part-attachment-bar.c - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#include "e-mail-part-attachment-bar.h" - -#define E_MAIL_PART_ATTACHMENT_BAR_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_MAIL_PART_ATTACHMENT_BAR, EMailPartAttachmentBarPrivate)) - -struct _EMailPartAttachmentBarPrivate { - EAttachmentStore *store; -}; - -G_DEFINE_TYPE ( - EMailPartAttachmentBar, - e_mail_part_attachment_bar, - E_TYPE_MAIL_PART) - -static void -mail_part_attachment_bar_dispose (GObject *object) -{ - EMailPartAttachmentBarPrivate *priv; - - priv = E_MAIL_PART_ATTACHMENT_BAR_GET_PRIVATE (object); - - if (priv->store) - e_attachment_store_remove_all (priv->store); - - g_clear_object (&priv->store); - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_mail_part_attachment_bar_parent_class)-> - dispose (object); -} - -static void -e_mail_part_attachment_bar_class_init (EMailPartAttachmentBarClass *class) -{ - GObjectClass *object_class; - - g_type_class_add_private ( - class, sizeof (EMailPartAttachmentBarPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->dispose = mail_part_attachment_bar_dispose; -} - -static void -e_mail_part_attachment_bar_init (EMailPartAttachmentBar *part) -{ - part->priv = E_MAIL_PART_ATTACHMENT_BAR_GET_PRIVATE (part); -} - -EMailPart * -e_mail_part_attachment_bar_new (CamelMimePart *mime_part, - const gchar *id) -{ - g_return_val_if_fail (id != NULL, NULL); - - return g_object_new ( - E_TYPE_MAIL_PART_ATTACHMENT_BAR, - "id", id, "mime-part", mime_part, NULL); -} - -EAttachmentStore * -e_mail_part_attachment_bar_get_store (EMailPartAttachmentBar *part) -{ - g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT_BAR (part), NULL); - - if (!part->priv->store) { - GtkTreeModel *tree_model; - - /* Create the store only on demand. The EMailParser runs in a dedicated - * thread, but the EAttachmentStore is a GtkWidget descendant, which should - * be manipulated only from the main/UI thread, thus postpone its creating - * until it's really needed, which might be during the EMailFormatter run, - * which runs from the main/UI thread. */ - tree_model = e_attachment_store_new (); - part->priv->store = E_ATTACHMENT_STORE (tree_model); - } - - return part->priv->store; -} - diff --git a/em-format/e-mail-part-attachment-bar.h b/em-format/e-mail-part-attachment-bar.h deleted file mode 100644 index e80a5bc..0000000 --- a/em-format/e-mail-part-attachment-bar.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * e-mail-part-attachment-bar.h - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifndef E_MAIL_PART_ATTACHMENT_BAR_H -#define E_MAIL_PART_ATTACHMENT_BAR_H - -#include <em-format/e-mail-part.h> - -/* Standard GObject macros */ -#define E_TYPE_MAIL_PART_ATTACHMENT_BAR \ - (e_mail_part_attachment_bar_get_type ()) -#define E_MAIL_PART_ATTACHMENT_BAR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_MAIL_PART_ATTACHMENT_BAR, EMailPartAttachmentBar)) -#define E_MAIL_PART_ATTACHMENT_BAR_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_MAIL_PART_ATTACHMENT_BAR, EMailPartAttachmentBarClass)) -#define E_IS_MAIL_PART_ATTACHMENT_BAR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_MAIL_PART_ATTACHMENT_BAR)) -#define E_IS_MAIL_PART_ATTACHMENT_BAR_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_MAIL_PART_ATTACHMENT_BAR)) -#define E_MAIL_PART_ATTACHMENT_BAR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_MAIL_PART_ATTACHMENT_BAR, EMailPartAttachmentBarClass)) - -#define E_MAIL_PART_ATTACHMENT_BAR_MIME_TYPE \ - "application/vnd.evolution.widget.attachment-bar" - -G_BEGIN_DECLS - -typedef struct _EMailPartAttachmentBar EMailPartAttachmentBar; -typedef struct _EMailPartAttachmentBarClass EMailPartAttachmentBarClass; -typedef struct _EMailPartAttachmentBarPrivate EMailPartAttachmentBarPrivate; - -struct _EMailPartAttachmentBar { - EMailPart parent; - EMailPartAttachmentBarPrivate *priv; -}; - -struct _EMailPartAttachmentBarClass { - EMailPartClass parent_class; -}; - -GType e_mail_part_attachment_bar_get_type - (void) G_GNUC_CONST; -EMailPart * e_mail_part_attachment_bar_new (CamelMimePart *mime_part, - const gchar *id); -EAttachmentStore * - e_mail_part_attachment_bar_get_store - (EMailPartAttachmentBar *part); - -G_END_DECLS - -#endif /* E_MAIL_PART_ATTACHMENT_BAR_H */ diff --git a/em-format/e-mail-part-attachment.c b/em-format/e-mail-part-attachment.c index e12e555..3490c3d 100644 --- a/em-format/e-mail-part-attachment.c +++ b/em-format/e-mail-part-attachment.c @@ -98,7 +98,7 @@ mail_part_attachment_finalize (GObject *object) { EMailPartAttachment *part = E_MAIL_PART_ATTACHMENT (object); - g_free (part->attachment_view_part_id); + g_free (part->part_id_with_attachment); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_mail_part_attachment_parent_class)-> diff --git a/em-format/e-mail-part-attachment.h b/em-format/e-mail-part-attachment.h index bdf859c..818ab30 100644 --- a/em-format/e-mail-part-attachment.h +++ b/em-format/e-mail-part-attachment.h @@ -52,7 +52,7 @@ struct _EMailPartAttachment { EMailPart parent; EMailPartAttachmentPrivate *priv; - gchar *attachment_view_part_id; + gchar *part_id_with_attachment; gboolean shown; const gchar *snoop_mime_type; diff --git a/em-format/e-mail-part-headers.c b/em-format/e-mail-part-headers.c index 5c494cd..1cc4ebb 100644 --- a/em-format/e-mail-part-headers.c +++ b/em-format/e-mail-part-headers.c @@ -218,29 +218,21 @@ mail_part_headers_constructed (GObject *object) static void mail_part_headers_bind_dom_element (EMailPart *part, - WebKitDOMElement *element) + GDBusProxy *web_extension, + guint64 page_id, + const gchar *element_id) { - WebKitDOMDocument *document; - WebKitDOMElement *photo; - gchar *addr, *uri; - - document = webkit_dom_node_get_owner_document ( - WEBKIT_DOM_NODE (element)); - photo = webkit_dom_document_get_element_by_id ( - document, "__evo-contact-photo"); - - /* Contact photos disabled, the <img> tag is not there. */ - if (photo == NULL) - return; - - addr = webkit_dom_element_get_attribute (photo, "data-mailaddr"); - uri = g_strdup_printf ("mail://contact-photo?mailaddr=%s", addr); - - webkit_dom_html_image_element_set_src ( - WEBKIT_DOM_HTML_IMAGE_ELEMENT (photo), uri); - - g_free (addr); - g_free (uri); + if (web_extension) { + g_dbus_proxy_call ( + web_extension, + "EMailPartHeadersBindDOMElement", + g_variant_new ("(ts)", page_id, element_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } } static void diff --git a/em-format/e-mail-part-secure-button.c b/em-format/e-mail-part-secure-button.c new file mode 100644 index 0000000..2455303 --- /dev/null +++ b/em-format/e-mail-part-secure-button.c @@ -0,0 +1,321 @@ +/* + * 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. + * + * 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 General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) +#include "certificate-manager.h" +#include "e-cert-db.h" +#endif + +#include "e-mail-part-secure-button.h" + +G_DEFINE_TYPE (EMailPartSecureButton, e_mail_part_secure_button, E_TYPE_MAIL_PART) + +const gchar *e_mail_formatter_secure_button_get_encrypt_description (CamelCipherValidityEncrypt status); +const gchar *e_mail_formatter_secure_button_get_sign_description (CamelCipherValiditySign status); + + +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) +static void +viewcert_clicked (GtkWidget *button, + GtkWidget *grid) +{ + CamelCipherCertInfo *info = g_object_get_data ((GObject *) button, "e-cert-info"); + ECert *ec = NULL; + + if (info->cert_data) + ec = e_cert_new (CERT_DupCertificate (info->cert_data)); + + if (ec != NULL) { + GtkWidget *dialog, *parent; + + parent = gtk_widget_get_toplevel (grid); + if (!parent || !GTK_IS_WINDOW (parent)) + parent = NULL; + + dialog = e_cert_manager_new_certificate_viewer ((GtkWindow *) parent, ec); + + gtk_widget_show (dialog); + g_signal_connect ( + dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + g_object_unref (ec); + } else { + g_warning ( + "can't find certificate for %s <%s>", + info->name ? info->name : "", + info->email ? info->email : ""); + } +} +#endif + +static void +info_response (GtkWidget *widget, + guint button, + gpointer user_data) +{ + gtk_widget_destroy (widget); +} + +static void +add_cert_table (GtkWidget *grid, + GQueue *certlist, + gpointer user_data) +{ + GList *head, *link; + GtkTable *table; + gint n = 0; + + table = (GtkTable *) gtk_table_new (certlist->length, 2, FALSE); + + head = g_queue_peek_head_link (certlist); + + for (link = head; link != NULL; link = g_list_next (link)) { + CamelCipherCertInfo *info = link->data; + gchar *la = NULL; + const gchar *l = NULL; + + if (info->name) { + if (info->email && strcmp (info->name, info->email) != 0) + l = la = g_strdup_printf ("%s <%s>", info->name, info->email); + else + l = info->name; + } else { + if (info->email) + l = info->email; + } + + if (l) { + GtkWidget *w; +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) + ECert *ec = NULL; +#endif + w = gtk_label_new (l); + gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); + g_free (la); + gtk_table_attach (table, w, 0, 1, n, n + 1, GTK_FILL, GTK_FILL, 3, 3); +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) + w = gtk_button_new_with_mnemonic (_("_View Certificate")); + gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3); + g_object_set_data ((GObject *) w, "e-cert-info", info); + g_signal_connect ( + w, "clicked", + G_CALLBACK (viewcert_clicked), grid); + + if (info->cert_data) + ec = e_cert_new (CERT_DupCertificate (info->cert_data)); + + if (ec == NULL) + gtk_widget_set_sensitive (w, FALSE); + else + g_object_unref (ec); +#else + w = gtk_label_new (_("This certificate is not viewable")); + gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3); +#endif + n++; + } + } + + gtk_container_add (GTK_CONTAINER (grid), GTK_WIDGET (table)); +} + +static void +secure_button_show_validity_dialog (EWebView *web_view, + CamelCipherValidity *validity) +{ + GtkBuilder *builder; + GtkWidget *grid, *w; + GtkWidget *dialog; + + g_return_if_fail (validity != NULL); + + /* Make sure our custom widget classes are registered with + * GType before we load the GtkBuilder definition file. */ + g_type_ensure (E_TYPE_DATE_EDIT); + + builder = gtk_builder_new (); + e_load_ui_builder_definition (builder, "mail-dialogs.ui"); + + dialog = e_builder_get_widget (builder, "message_security_dialog"); + + w = gtk_widget_get_toplevel (GTK_WIDGET (web_view)); + if (GTK_IS_WINDOW (w)) + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (w)); + + grid = e_builder_get_widget (builder, "signature_grid"); + w = gtk_label_new (e_mail_formatter_secure_button_get_sign_description (validity->sign.status)); + gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); + gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); + gtk_container_add (GTK_CONTAINER (grid), w); + if (validity->sign.description) { + GtkTextBuffer *buffer; + + buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_set_text ( + buffer, validity->sign.description, + strlen (validity->sign.description)); + w = g_object_new ( + gtk_scrolled_window_get_type (), + "hscrollbar_policy", GTK_POLICY_AUTOMATIC, + "vscrollbar_policy", GTK_POLICY_AUTOMATIC, + "shadow_type", GTK_SHADOW_IN, + "expand", TRUE, + "child", g_object_new (gtk_text_view_get_type (), + "buffer", buffer, + "cursor_visible", FALSE, + "editable", FALSE, + NULL), + "width_request", 500, + "height_request", 80, + NULL); + g_object_unref (buffer); + + gtk_container_add (GTK_CONTAINER (grid), w); + } + + if (!g_queue_is_empty (&validity->sign.signers)) + add_cert_table (grid, &validity->sign.signers, NULL); + + gtk_widget_show_all (grid); + + grid = e_builder_get_widget (builder, "encryption_grid"); + w = gtk_label_new (e_mail_formatter_secure_button_get_encrypt_description (validity->encrypt.status)); + gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); + gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); + gtk_container_add (GTK_CONTAINER (grid), w); + if (validity->encrypt.description) { + GtkTextBuffer *buffer; + + buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_set_text ( + buffer, validity->encrypt.description, + strlen (validity->encrypt.description)); + w = g_object_new ( + gtk_scrolled_window_get_type (), + "hscrollbar_policy", GTK_POLICY_AUTOMATIC, + "vscrollbar_policy", GTK_POLICY_AUTOMATIC, + "shadow_type", GTK_SHADOW_IN, + "expand", TRUE, + "child", g_object_new (gtk_text_view_get_type (), + "buffer", buffer, + "cursor_visible", FALSE, + "editable", FALSE, + NULL), + "width_request", 500, + "height_request", 80, + NULL); + g_object_unref (buffer); + + gtk_container_add (GTK_CONTAINER (grid), w); + } + + if (!g_queue_is_empty (&validity->encrypt.encrypters)) + add_cert_table (grid, &validity->encrypt.encrypters, NULL); + + gtk_widget_show_all (grid); + + g_object_unref (builder); + + g_signal_connect ( + dialog, "response", + G_CALLBACK (info_response), NULL); + + gtk_widget_show (dialog); +} + +static void +secure_button_clicked_cb (EWebView *web_view, + const gchar *element_class, + const gchar *element_value, + const GtkAllocation *element_position, + gpointer user_data) +{ + EMailPart *mail_part = user_data; + GList *head, *link; + gboolean can_use; + gchar *tmp; + + g_return_if_fail (E_IS_MAIL_PART_SECURE_BUTTON (mail_part)); + + tmp = g_strdup_printf ("%p:", mail_part); + can_use = element_value && g_str_has_prefix (element_value, tmp); + if (can_use) + element_value += strlen (tmp); + g_free (tmp); + + if (!can_use) + return; + + head = g_queue_peek_head_link (&mail_part->validities); + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPartValidityPair *pair = link->data; + + if (!pair) + continue; + + tmp = g_strdup_printf ("%p", pair->validity); + can_use = g_strcmp0 (element_value, tmp) == 0; + g_free (tmp); + + if (can_use) { + secure_button_show_validity_dialog (web_view, pair->validity); + break; + } + } +} + +static void +mail_part_secure_button_web_view_loaded (EMailPart *mail_part, + EWebView *web_view) +{ + g_return_if_fail (E_IS_MAIL_PART_SECURE_BUTTON (mail_part)); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + e_web_view_register_element_clicked (web_view, "secure-button", secure_button_clicked_cb, mail_part); +} + +static void +e_mail_part_secure_button_class_init (EMailPartSecureButtonClass *class) +{ + EMailPartClass *mail_part_class; + + mail_part_class = E_MAIL_PART_CLASS (class); + mail_part_class->web_view_loaded = mail_part_secure_button_web_view_loaded; +} + +static void +e_mail_part_secure_button_init (EMailPartSecureButton *part) +{ +} + +EMailPart * +e_mail_part_secure_button_new (CamelMimePart *mime_part, + const gchar *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return g_object_new ( + E_TYPE_MAIL_PART_SECURE_BUTTON, + "id", id, "mime-part", mime_part, NULL); +} diff --git a/em-format/e-mail-part-secure-button.h b/em-format/e-mail-part-secure-button.h new file mode 100644 index 0000000..b40a0d3 --- /dev/null +++ b/em-format/e-mail-part-secure-button.h @@ -0,0 +1,61 @@ +/* + * 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. + * + * 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 General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef E_MAIL_PART_SECURE_BUTTON_H +#define E_MAIL_PART_SECURE_BUTTON_H + +#include <em-format/e-mail-part.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PART_SECURE_BUTTON \ + (e_mail_part_secure_button_get_type ()) +#define E_MAIL_PART_SECURE_BUTTON(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PART_SECURE_BUTTON, EMailPartSecureButton)) +#define E_MAIL_PART_SECURE_BUTTON_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PART_SECURE_BUTTON, EMailPartSecureButtonClass)) +#define E_IS_MAIL_PART_SECURE_BUTTON(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PART_SECURE_BUTTON)) +#define E_IS_MAIL_PART_SECURE_BUTTON_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PART_SECURE_BUTTON)) +#define E_MAIL_PART_SECURE_BUTTON_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PART_SECURE_BUTTON, EMailPartSecureButtonClass)) + +G_BEGIN_DECLS + +typedef struct _EMailPartSecureButton EMailPartSecureButton; +typedef struct _EMailPartSecureButtonClass EMailPartSecureButtonClass; +typedef struct _EMailPartSecureButtonPrivate EMailPartSecureButtonPrivate; + +struct _EMailPartSecureButton { + EMailPart parent; + EMailPartSecureButtonPrivate *priv; +}; + +struct _EMailPartSecureButtonClass { + EMailPartClass parent_class; +}; + +GType e_mail_part_secure_button_get_type (void) G_GNUC_CONST; +EMailPart * e_mail_part_secure_button_new (CamelMimePart *mime_part, + const gchar *id); + +G_END_DECLS + +#endif /* E_MAIL_PART_SECURE_BUTTON_H */ diff --git a/em-format/e-mail-part-utils.c b/em-format/e-mail-part-utils.c index b0899ee..0568a35 100644 --- a/em-format/e-mail-part-utils.c +++ b/em-format/e-mail-part-utils.c @@ -488,6 +488,11 @@ e_mail_part_build_uri (CamelFolder *folder, g_free (escaped); break; } + case G_TYPE_POINTER: { + gpointer val = va_arg (ap, gpointer); + tmp2 = g_strdup_printf ("%s%c%s=%p", tmp, separator, name, val); + break; + } default: g_warning ("Invalid param type %s", g_type_name (type)); va_end (ap); diff --git a/em-format/e-mail-part.c b/em-format/e-mail-part.c index 432481c..fbfb332 100644 --- a/em-format/e-mail-part.c +++ b/em-format/e-mail-part.c @@ -585,17 +585,36 @@ e_mail_part_set_is_attachment (EMailPart *part, void e_mail_part_bind_dom_element (EMailPart *part, - WebKitDOMElement *element) + GDBusProxy *web_extension, + guint64 page_id, + const gchar *element_id) { EMailPartClass *class; g_return_if_fail (E_IS_MAIL_PART (part)); - g_return_if_fail (WEBKIT_DOM_IS_ELEMENT (element)); + g_return_if_fail (web_extension); + g_return_if_fail (page_id != 0); + g_return_if_fail (element_id && *element_id); class = E_MAIL_PART_GET_CLASS (part); if (class->bind_dom_element != NULL) - class->bind_dom_element (part, element); + class->bind_dom_element (part, web_extension, page_id, element_id); +} + +void +e_mail_part_web_view_loaded (EMailPart *part, + EWebView *web_view) +{ + EMailPartClass *klass; + + g_return_if_fail (E_IS_MAIL_PART (part)); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + klass = E_MAIL_PART_GET_CLASS (part); + + if (klass->web_view_loaded) + klass->web_view_loaded (part, web_view); } static EMailPartValidityPair * diff --git a/em-format/e-mail-part.h b/em-format/e-mail-part.h index cb46042..72e17ad 100644 --- a/em-format/e-mail-part.h +++ b/em-format/e-mail-part.h @@ -19,7 +19,6 @@ #define E_MAIL_PART_H #include <camel/camel.h> -#include <webkit/webkitdom.h> #include <e-util/e-util.h> @@ -87,7 +86,11 @@ struct _EMailPartClass { GObjectClass parent_class; void (*bind_dom_element) (EMailPart *part, - WebKitDOMElement *element); + GDBusProxy *web_extension, + guint64 page_id, + const gchar *element_id); + void (*web_view_loaded) (EMailPart *part, + EWebView *web_view); }; GType e_mail_part_get_type (void) G_GNUC_CONST; @@ -121,7 +124,11 @@ gboolean e_mail_part_get_is_attachment (EMailPart *part); void e_mail_part_set_is_attachment (EMailPart *part, gboolean is_attachment); void e_mail_part_bind_dom_element (EMailPart *part, - WebKitDOMElement *element); + GDBusProxy *web_extension, + guint64 page_id, + const gchar *element_id); +void e_mail_part_web_view_loaded (EMailPart *part, + EWebView *web_view); void e_mail_part_update_validity (EMailPart *part, CamelCipherValidity *validity, EMailPartValidityFlags validity_type); diff --git a/mail/Makefile.am b/mail/Makefile.am index 06aa259..acf784f 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -52,6 +52,7 @@ libevolution_mail_la_CPPFLAGS = \ $(NULL) mailinclude_HEADERS = \ + e-cid-request.h \ e-http-request.h \ e-mail.h \ e-mail-account-manager.h \ @@ -135,6 +136,7 @@ mailinclude_HEADERS = \ message-list.h libevolution_mail_la_SOURCES = \ + e-cid-request.c \ e-http-request.c \ e-mail-account-manager.c \ e-mail-account-store.c \ diff --git a/mail/e-cid-request.c b/mail/e-cid-request.c new file mode 100644 index 0000000..e7529f7 --- /dev/null +++ b/mail/e-cid-request.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include "e-mail-display.h" +#include "e-cid-request.h" + +struct _ECidRequestPrivate { + gint dummy; +}; + +static void e_cid_request_content_request_init (EContentRequestInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (ECidRequest, e_cid_request, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_cid_request_content_request_init)) + +static gboolean +e_cid_request_can_process_uri (EContentRequest *request, + const gchar *uri) +{ + g_return_val_if_fail (E_IS_CID_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + + return g_ascii_strncasecmp (uri, "cid:", 4) == 0; +} + +static gboolean +e_cid_request_process_sync (EContentRequest *request, + const gchar *uri, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error) +{ + EMailDisplay *display; + EMailPartList *part_list; + EMailPart *part; + GByteArray *byte_array; + CamelStream *output_stream; + CamelDataWrapper *dw; + CamelMimePart *mime_part; + gboolean success = FALSE; + + g_return_val_if_fail (E_IS_CID_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + if (!E_IS_MAIL_DISPLAY (requester)) + return FALSE; + + display = E_MAIL_DISPLAY (requester); + + part_list = e_mail_display_get_part_list (display); + if (!part_list) + return FALSE; + + part = e_mail_part_list_ref_part (part_list, uri); + if (!part) + return FALSE; + + mime_part = e_mail_part_ref_mime_part (part); + dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); + + g_return_val_if_fail (dw != NULL, FALSE); + + byte_array = g_byte_array_new (); + output_stream = camel_stream_mem_new (); + + /* We retain ownership of the byte array. */ + camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (output_stream), byte_array); + + if (camel_data_wrapper_decode_to_stream_sync (dw, output_stream, cancellable, error)) { + GBytes *bytes; + gchar *mime_type; + + bytes = g_byte_array_free_to_bytes (byte_array); + + success = TRUE; + + *out_stream = g_memory_input_stream_new_from_bytes (bytes); + *out_stream_length = g_bytes_get_size (bytes); + + mime_type = camel_data_wrapper_get_mime_type (dw); + if (mime_type && *mime_type) + *out_mime_type = mime_type; + else { + g_free (mime_type); + *out_mime_type = g_strdup (e_mail_part_get_mime_type (part)); + } + + g_bytes_unref (bytes); + } + + g_object_unref (mime_part); + g_object_unref (part); + + return success; +} + +static void +e_cid_request_content_request_init (EContentRequestInterface *iface) +{ + iface->can_process_uri = e_cid_request_can_process_uri; + iface->process_sync = e_cid_request_process_sync; +} + +static void +e_cid_request_class_init (ECidRequestClass *class) +{ + g_type_class_add_private (class, sizeof (ECidRequestPrivate)); +} + +static void +e_cid_request_init (ECidRequest *request) +{ + request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_CID_REQUEST, ECidRequestPrivate); +} + +EContentRequest * +e_cid_request_new (void) +{ + return g_object_new (E_TYPE_CID_REQUEST, NULL); +} diff --git a/mail/e-cid-request.h b/mail/e-cid-request.h new file mode 100644 index 0000000..0ed6e4b --- /dev/null +++ b/mail/e-cid-request.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef E_CID_REQUEST_H +#define E_CID_REQUEST_H + +#include <e-util/e-util.h> + +/* Standard GObject macros */ +#define E_TYPE_CID_REQUEST \ + (e_cid_request_get_type ()) +#define E_CID_REQUEST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CID_REQUEST, ECidRequest)) +#define E_CID_REQUEST_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CID_REQUEST, ECidRequestClass)) +#define E_IS_CID_REQUEST(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CID_REQUEST)) +#define E_IS_CID_REQUEST_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CID_REQUEST)) +#define E_CID_REQUEST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CID_REQUEST, ECidRequestClass)) + +G_BEGIN_DECLS + +typedef struct _ECidRequest ECidRequest; +typedef struct _ECidRequestClass ECidRequestClass; +typedef struct _ECidRequestPrivate ECidRequestPrivate; + +struct _ECidRequest { + GObject parent; + ECidRequestPrivate *priv; +}; + +struct _ECidRequestClass { + GObjectClass parent; +}; + +GType e_cid_request_get_type (void) G_GNUC_CONST; +EContentRequest * + e_cid_request_new (void); + +G_END_DECLS + +#endif /* E_CID_REQUEST_H */ diff --git a/mail/e-http-request.c b/mail/e-http-request.c index 488732a..9e7711b 100644 --- a/mail/e-http-request.c +++ b/mail/e-http-request.c @@ -15,9 +15,10 @@ * */ -#include "e-http-request.h" - +#ifdef HAVE_CONFIG_H #include <config.h> +#endif + #include <string.h> #define LIBSOUP_USE_UNSTABLE_REQUEST_API @@ -25,7 +26,7 @@ #include <libsoup/soup-requester.h> #include <libsoup/soup-request-http.h> #include <camel/camel.h> -#include <webkit/webkit.h> +#include <webkit2/webkit2.h> #include <e-util/e-util.h> #include <libemail-engine/libemail-engine.h> @@ -35,19 +36,31 @@ #include <shell/e-shell.h> #include "e-mail-ui-session.h" +#include "e-http-request.h" #define d(x) -#define E_HTTP_REQUEST_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_HTTP_REQUEST, EHTTPRequestPrivate)) - struct _EHTTPRequestPrivate { - gchar *content_type; - gint content_length; + gint dummy; }; -G_DEFINE_TYPE (EHTTPRequest, e_http_request, SOUP_TYPE_REQUEST) +static void e_http_request_content_request_init (EContentRequestInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (EHTTPRequest, e_http_request, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_http_request_content_request_init)) + +static gboolean +e_http_request_can_process_uri (EContentRequest *request, + const gchar *uri) +{ + g_return_val_if_fail (E_IS_HTTP_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + + return g_ascii_strncasecmp (uri, "evo-http:", 9) == 0 || + g_ascii_strncasecmp (uri, "evo-https:", 10) == 0 || + g_ascii_strncasecmp (uri, "http:", 5) == 0 || + g_ascii_strncasecmp (uri, "https:", 6) == 0; +} static gssize copy_stream_to_stream (GIOStream *file_io_stream, @@ -162,16 +175,18 @@ http_request_cancelled_cb (GCancellable *cancellable, soup_session_abort (session); } -static void -handle_http_request (GSimpleAsyncResult *res, - GObject *source_object, - GCancellable *cancellable) +static gboolean +e_http_request_process_sync (EContentRequest *request, + const gchar *uri, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error) { - EHTTPRequestPrivate *priv; SoupURI *soup_uri; - SoupRequest *soup_request; - SoupSession *soup_session; - gchar *evo_uri, *uri; + gchar *evo_uri, *use_uri; gchar *mail_uri = NULL; GInputStream *stream; gboolean force_load_images = FALSE; @@ -183,15 +198,16 @@ handle_http_request (GSimpleAsyncResult *res, CamelDataCache *cache; GIOStream *cache_stream; gint uri_len; + gboolean success = FALSE; - if (g_cancellable_is_cancelled (cancellable)) - return; + g_return_val_if_fail (E_IS_HTTP_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); - priv = E_HTTP_REQUEST_GET_PRIVATE (source_object); + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; - soup_request = SOUP_REQUEST (source_object); - soup_session = soup_request_get_session (soup_request); - soup_uri = soup_request_get_uri (soup_request); + soup_uri = soup_uri_new (uri); + g_return_val_if_fail (soup_uri != NULL, FALSE); /* Remove the __evo-mail query */ soup_query = soup_uri_get_query (soup_uri); @@ -224,24 +240,31 @@ handle_http_request (GSimpleAsyncResult *res, /* Remove the "evo-" prefix from scheme */ uri_len = (evo_uri != NULL) ? strlen (evo_uri) : 0; - uri = NULL; + use_uri = NULL; if (evo_uri != NULL && (uri_len > 5)) { + gint inc = 0; + + if (g_str_has_prefix (evo_uri, "evo-")) + inc = 4; /* Remove trailing "?" if there is no URI query */ if (evo_uri[uri_len - 1] == '?') { - uri = g_strndup (evo_uri + 4, uri_len - 5); + use_uri = g_strndup (evo_uri + inc, uri_len - 1 - inc); } else { - uri = g_strdup (evo_uri + 4); + use_uri = g_strdup (evo_uri + inc); } - g_free (evo_uri); } - g_return_if_fail (uri && *uri); + g_free (evo_uri); + + g_return_val_if_fail (use_uri && *use_uri, FALSE); + + *out_stream_length = -1; /* Use MD5 hash of the URI as a filname of the resourec cache file. * We were previously using the URI as a filename but the URI is * sometimes too long for a filename. */ - uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); + uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, use_uri, -1); /* Open Evolution's cache */ user_cache_dir = e_get_user_cache_dir (); @@ -251,27 +274,23 @@ handle_http_request (GSimpleAsyncResult *res, camel_data_cache_set_expire_access (cache, 2 * 60 * 60); cache_stream = camel_data_cache_get (cache, "http", uri_md5, NULL); - } else { + } else cache_stream = NULL; - } - /* Found item in cache! */ if (cache_stream != NULL) { gssize len; stream = g_memory_input_stream_new (); - len = copy_stream_to_stream ( - cache_stream, - G_MEMORY_INPUT_STREAM (stream), cancellable); - priv->content_length = len; + len = copy_stream_to_stream (cache_stream, G_MEMORY_INPUT_STREAM (stream), cancellable); + *out_stream_length = len; g_object_unref (cache_stream); /* When succesfully read some data from cache then * get mimetype and return the stream to WebKit. * Otherwise try to fetch the resource again from the network. */ - if ((len != -1) && (priv->content_length > 0)) { + if (len != -1 && *out_stream_length > 0) { GFile *file; GFileInfo *info; gchar *path; @@ -284,13 +303,12 @@ handle_http_request (GSimpleAsyncResult *res, 0, cancellable, NULL); if (info) { - priv->content_type = g_strdup ( - g_file_info_get_content_type (info)); + *out_mime_type = g_strdup (g_file_info_get_content_type (info)); d ( printf ("'%s' found in cache (%d bytes, %s)\n", - uri, priv->content_length, - priv->content_type)); + use_uri, (gint) *out_stream_length, + *out_mime_type)); } g_clear_object (&info); @@ -298,12 +316,12 @@ handle_http_request (GSimpleAsyncResult *res, g_free (path); /* Set result and quit the thread */ - g_simple_async_result_set_op_res_gpointer ( - res, stream, g_object_unref); + *out_stream = stream; + success = TRUE; goto cleanup; } else { - d (printf ("Failed to load '%s' from cache.\n", uri)); + d (printf ("Failed to load '%s' from cache.\n", use_uri)); g_object_unref (stream); } } @@ -337,7 +355,6 @@ handle_http_request (GSimpleAsyncResult *res, CamelInternetAddress *addr; CamelMimeMessage *message; gboolean known_address = FALSE; - GError *error = NULL; shell_backend = e_shell_get_backend_by_name (shell, "mail"); @@ -347,14 +364,13 @@ handle_http_request (GSimpleAsyncResult *res, message = e_mail_part_list_get_message (part_list); addr = camel_mime_message_get_from (message); - e_mail_ui_session_check_known_address_sync ( + if (!e_mail_ui_session_check_known_address_sync ( E_MAIL_UI_SESSION (session), addr, FALSE, cancellable, - &known_address, &error); - - if (error != NULL) { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_error_free (error); + &known_address, error)) { + g_object_unref (part_list); + g_free (decoded_uri); + goto cleanup; } if (known_address) @@ -368,20 +384,19 @@ handle_http_request (GSimpleAsyncResult *res, if ((image_policy == E_IMAGE_LOADING_POLICY_ALWAYS) || force_load_images) { - + ESource *proxy_source; SoupSession *temp_session; SoupMessage *message; GIOStream *cache_stream; - GError *error; GMainContext *context; gulong cancelled_id = 0; - if (g_cancellable_is_cancelled (cancellable)) + if (g_cancellable_set_error_if_cancelled (cancellable, error)) goto cleanup; - message = soup_message_new (SOUP_METHOD_GET, uri); + message = soup_message_new (SOUP_METHOD_GET, use_uri); if (!message) { - g_debug ("%s: Skipping invalid URI '%s'", G_STRFUNC, uri); + g_debug ("%s: Skipping invalid URI '%s'", G_STRFUNC, use_uri); goto cleanup; } @@ -391,10 +406,15 @@ handle_http_request (GSimpleAsyncResult *res, temp_session = soup_session_new_with_options ( SOUP_SESSION_TIMEOUT, 90, NULL); - e_binding_bind_property ( - soup_session, "proxy-resolver", - temp_session, "proxy-resolver", - G_BINDING_SYNC_CREATE); + proxy_source = e_source_registry_ref_builtin_proxy (e_shell_get_registry (shell)); + + g_object_set ( + temp_session, + SOUP_SESSION_PROXY_RESOLVER, + G_PROXY_RESOLVER (proxy_source), + NULL); + + g_object_unref (proxy_source); soup_message_headers_append ( message->request_headers, @@ -409,65 +429,68 @@ handle_http_request (GSimpleAsyncResult *res, g_cancellable_disconnect (cancellable, cancelled_id); if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) { - g_debug ("Failed to request %s (code %d)", uri, message->status_code); + g_debug ("Failed to request %s (code %d)", use_uri, message->status_code); g_object_unref (message); g_object_unref (temp_session); g_main_context_unref (context); goto cleanup; } - /* Write the response body to cache */ - error = NULL; if (cache) { + GError *local_error = NULL; + cache_stream = camel_data_cache_add ( - cache, "http", uri_md5, &error); - if (error != NULL) { + cache, "http", uri_md5, &local_error); + if (local_error) { g_warning ( "Failed to create cache file for '%s': %s", - uri, error->message); - g_clear_error (&error); + uri, local_error->message); + g_clear_error (&local_error); } else { GOutputStream *output_stream; output_stream = g_io_stream_get_output_stream (cache_stream); - g_output_stream_write_all ( + success = g_output_stream_write_all ( output_stream, message->response_body->data, message->response_body->length, - NULL, cancellable, &error); + NULL, cancellable, &local_error); g_io_stream_close (cache_stream, NULL, NULL); g_object_unref (cache_stream); - if (error != NULL) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (local_error != NULL) { + if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ( "Failed to write data to cache stream: %s", - error->message); - g_clear_error (&error); + local_error->message); + g_clear_error (&local_error); g_object_unref (message); g_object_unref (temp_session); g_main_context_unref (context); goto cleanup; } + + if (success) { + /* Send the response body to WebKit */ + stream = g_memory_input_stream_new_from_data ( + g_memdup ( + message->response_body->data, + message->response_body->length), + message->response_body->length, + (GDestroyNotify) g_free); + + *out_stream = stream; + *out_stream_length = message->response_body->length; + *out_mime_type = g_strdup ( + soup_message_headers_get_content_type ( + message->response_headers, NULL)); + } } } - /* Send the response body to WebKit */ - stream = g_memory_input_stream_new_from_data ( - g_memdup ( - message->response_body->data, - message->response_body->length), - message->response_body->length, - (GDestroyNotify) g_free); - - priv->content_length = message->response_body->length; - priv->content_type = g_strdup ( - soup_message_headers_get_content_type ( - message->response_headers, NULL)); - g_object_unref (message); g_object_unref (temp_session); g_main_context_unref (context); @@ -476,150 +499,42 @@ handle_http_request (GSimpleAsyncResult *res, "Content-Type: %s\n" "Content-Length: %d bytes\n" "URI MD5: %s:\n", - uri, priv->content_type, - priv->content_length, uri_md5)); - - g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref); - - goto cleanup; + use_uri, *out_mime_type ? *out_mime_type : "[null]", + (gint) *out_stream_length, uri_md5)); } -cleanup: + cleanup: g_clear_object (&cache); - g_free (uri); + g_free (use_uri); g_free (uri_md5); g_free (mail_uri); -} - -static void -http_request_finalize (GObject *object) -{ - EHTTPRequest *request = E_HTTP_REQUEST (object); - - if (request->priv->content_type) { - g_free (request->priv->content_type); - request->priv->content_type = NULL; - } - - G_OBJECT_CLASS (e_http_request_parent_class)->finalize (object); -} + soup_uri_free (soup_uri); -static gboolean -http_request_check_uri (SoupRequest *request, - SoupURI *uri, - GError **error) -{ - return ((strcmp (uri->scheme, "evo-http") == 0) || - (strcmp (uri->scheme, "evo-https") == 0)); + return success; } static void -http_request_send_async (SoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - d ({ - const gchar *soup_query; - SoupURI *uri; - - uri = soup_request_get_uri (request); - soup_query = soup_uri_get_query (uri); - - if (soup_query) { - gchar *uri_str; - GHashTable *query; - - query = soup_form_decode (soup_uri_get_query (uri)); - uri_str = soup_uri_to_string (uri, FALSE); - printf ("received request for %s\n", uri_str); - g_free (uri_str); - g_hash_table_destroy (query); - } - }); - - simple = g_simple_async_result_new ( - G_OBJECT (request), callback, - user_data, http_request_send_async); - - g_simple_async_result_set_check_cancellable (simple, cancellable); - - e_util_run_simple_async_result_in_thread ( - simple, handle_http_request, - cancellable); - - g_object_unref (simple); -} - -static GInputStream * -http_request_send_finish (SoupRequest *request, - GAsyncResult *result, - GError **error) -{ - GInputStream *stream; - - stream = g_simple_async_result_get_op_res_gpointer ( - G_SIMPLE_ASYNC_RESULT (result)); - - /* Reset the stream before passing it back to webkit */ - if (stream && G_IS_SEEKABLE (stream)) - g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL); - - if (!stream) /* We must always return something */ - stream = g_memory_input_stream_new (); - else - g_object_ref (stream); - - return stream; -} - -static goffset -http_request_get_content_length (SoupRequest *request) -{ - EHTTPRequest *efr = E_HTTP_REQUEST (request); - - d (printf ("Content-Length: %d bytes\n", efr->priv->content_length)); - return efr->priv->content_length; -} - -static const gchar * -http_request_get_content_type (SoupRequest *request) +e_http_request_content_request_init (EContentRequestInterface *iface) { - EHTTPRequest *efr = E_HTTP_REQUEST (request); - - d (printf ("Content-Type: %s\n", efr->priv->content_type)); - - return efr->priv->content_type; + iface->can_process_uri = e_http_request_can_process_uri; + iface->process_sync = e_http_request_process_sync; } -static const gchar *data_schemes[] = { "evo-http", "evo-https", NULL }; - static void e_http_request_class_init (EHTTPRequestClass *class) { - GObjectClass *object_class; - SoupRequestClass *request_class; - g_type_class_add_private (class, sizeof (EHTTPRequestPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = http_request_finalize; - - request_class = SOUP_REQUEST_CLASS (class); - request_class->schemes = data_schemes; - request_class->send_async = http_request_send_async; - request_class->send_finish = http_request_send_finish; - request_class->get_content_type = http_request_get_content_type; - request_class->get_content_length = http_request_get_content_length; - request_class->check_uri = http_request_check_uri; } static void e_http_request_init (EHTTPRequest *request) { - request->priv = E_HTTP_REQUEST_GET_PRIVATE (request); + request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_HTTP_REQUEST, EHTTPRequestPrivate); } +EContentRequest * +e_http_request_new (void) +{ + return g_object_new (E_TYPE_HTTP_REQUEST, NULL); +} diff --git a/mail/e-http-request.h b/mail/e-http-request.h index de4cb23..bdbf009 100644 --- a/mail/e-http-request.h +++ b/mail/e-http-request.h @@ -18,10 +18,7 @@ #ifndef E_HTTP_REQUEST_H #define E_HTTP_REQUEST_H -#define LIBSOUP_USE_UNSTABLE_REQUEST_API - -#include <libsoup/soup.h> -#include <libsoup/soup-request.h> +#include <e-util/e-util.h> /* Standard GObject macros */ #define E_TYPE_HTTP_REQUEST \ @@ -49,15 +46,18 @@ typedef struct _EHTTPRequestClass EHTTPRequestClass; typedef struct _EHTTPRequestPrivate EHTTPRequestPrivate; struct _EHTTPRequest { - SoupRequest parent; + GObject parent; EHTTPRequestPrivate *priv; }; struct _EHTTPRequestClass { - SoupRequestClass parent; + GObjectClass parent; }; GType e_http_request_get_type (void) G_GNUC_CONST; +EContentRequest * + e_http_request_new (void); + G_END_DECLS diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c index 1130c9c..9293803 100644 --- a/mail/e-mail-browser.c +++ b/mail/e-mail-browser.c @@ -607,6 +607,7 @@ mail_browser_constructed (GObject *object) EShellBackend *shell_backend; EShell *shell; EFocusTracker *focus_tracker; + EAttachmentStore *attachment_store; GtkAccelGroup *accel_group; GtkActionGroup *action_group; GtkAction *action; @@ -755,6 +756,18 @@ mail_browser_constructed (GObject *object) browser->priv->preview_pane, TRUE, TRUE, 0); + attachment_store = e_mail_display_get_attachment_store (E_MAIL_DISPLAY (display)); + widget = GTK_WIDGET (e_mail_display_get_attachment_view (E_MAIL_DISPLAY (display))); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + e_binding_bind_property_full ( + attachment_store, "num-attachments", + widget, "visible", + G_BINDING_SYNC_CREATE, + e_attachment_store_transform_num_attachments_to_visible_boolean, + NULL, NULL, NULL); + id = "org.gnome.evolution.mail.browser"; e_plugin_ui_register_manager (ui_manager, id, object); e_plugin_ui_enable_manager (ui_manager, id); @@ -766,47 +779,9 @@ static gboolean mail_browser_key_press_event (GtkWidget *widget, GdkEventKey *event) { - EMailDisplay *mail_display; - - g_return_val_if_fail (E_IS_MAIL_BROWSER (widget), FALSE); - - mail_display = e_mail_reader_get_mail_display (E_MAIL_READER (widget)); - - switch (event->keyval) { - case GDK_KEY_Escape: - e_mail_browser_close (E_MAIL_BROWSER (widget)); - return TRUE; - - case GDK_KEY_Home: - case GDK_KEY_Left: - case GDK_KEY_Up: - case GDK_KEY_Right: - case GDK_KEY_Down: - case GDK_KEY_Next: - case GDK_KEY_End: - case GDK_KEY_Begin: - /* If Caret mode is enabled don't try to process these keys */ - if (e_web_view_get_caret_mode (E_WEB_VIEW (mail_display))) - break; - case GDK_KEY_Prior: - if (!e_mail_display_needs_key (mail_display, FALSE) && - webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (mail_display)) != - webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (mail_display))) { - WebKitDOMDocument *document; - WebKitDOMDOMWindow *window; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (mail_display)); - window = webkit_dom_document_get_default_view (document); - - /* Workaround WebKit bug for key navigation, when inner IFRAME is focused. - * EMailView's inner IFRAMEs have disabled scrolling, but WebKit doesn't post - * key navigation events to parent's frame, thus the view doesn't scroll. - * This is a poor workaround for this issue, the main frame is focused, - * which has scrolling enabled. - */ - webkit_dom_dom_window_focus (window); - } - break; + if (event->keyval == GDK_KEY_Escape) { + e_mail_browser_close (E_MAIL_BROWSER (widget)); + return TRUE; } /* Chain up to parent's key_press_event() method. */ diff --git a/mail/e-mail-config-identity-page.c b/mail/e-mail-config-identity-page.c index 8b1a8fe..5baa5f5 100644 --- a/mail/e-mail-config-identity-page.c +++ b/mail/e-mail-config-identity-page.c @@ -80,17 +80,35 @@ mail_config_identity_page_is_email (const gchar *email_address) } static void +mail_config_identity_page_signature_editor_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GtkWidget *editor; + GError *error = NULL; + + g_return_if_fail (result != NULL); + + editor = e_mail_signature_editor_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create signature editor: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + gtk_window_set_position (GTK_WINDOW (editor), GTK_WIN_POS_CENTER); + gtk_widget_show (editor); + } +} + +static void mail_config_identity_page_add_signature_cb (GtkButton *button, EMailConfigIdentityPage *page) { ESourceRegistry *registry; - GtkWidget *editor; registry = e_mail_config_identity_page_get_registry (page); - editor = e_mail_signature_editor_new (registry, NULL); - gtk_window_set_position (GTK_WINDOW (editor), GTK_WIN_POS_CENTER); - gtk_widget_show (editor); + e_mail_signature_editor_new (registry, NULL, + mail_config_identity_page_signature_editor_created_cb, NULL); } static void diff --git a/mail/e-mail-display-popup-extension.c b/mail/e-mail-display-popup-extension.c index e95dc3e..2f9e919 100644 --- a/mail/e-mail-display-popup-extension.c +++ b/mail/e-mail-display-popup-extension.c @@ -33,14 +33,14 @@ e_mail_display_popup_extension_default_init (EMailDisplayPopupExtensionInterface * e_mail_display_popup_extension_update_actions: * * @extension: An object derived from #EMailDisplayPopupExtension - * @context: A #WebKitHitTestResult describing context of the popup menu + * @popup_document_uri: Document URI on top of which the popup menu had been invoked * * When #EMailDisplay is about to display a popup menu, it calls this function * on every extension so that they can add their items to the menu. */ void e_mail_display_popup_extension_update_actions (EMailDisplayPopupExtension *extension, - WebKitHitTestResult *context) + const gchar *popup_document_uri) { EMailDisplayPopupExtensionInterface *iface; @@ -49,5 +49,5 @@ e_mail_display_popup_extension_update_actions (EMailDisplayPopupExtension *exten iface = E_MAIL_DISPLAY_POPUP_EXTENSION_GET_INTERFACE (extension); g_return_if_fail (iface->update_actions != NULL); - iface->update_actions (extension, context); + iface->update_actions (extension, popup_document_uri); } diff --git a/mail/e-mail-display-popup-extension.h b/mail/e-mail-display-popup-extension.h index 67c1374..bedd23e 100644 --- a/mail/e-mail-display-popup-extension.h +++ b/mail/e-mail-display-popup-extension.h @@ -19,7 +19,6 @@ #define E_MAIL_DISPLAY_POPUP_EXTENSION_H #include <glib-object.h> -#include <webkit/webkit.h> /* Standard GObject macros */ #define E_TYPE_MAIL_DISPLAY_POPUP_EXTENSION \ @@ -49,14 +48,14 @@ struct _EMailDisplayPopupExtensionInterface { GTypeInterface parent_interface; void (*update_actions) (EMailDisplayPopupExtension *extension, - WebKitHitTestResult *context); + const gchar *popup_document_uri); }; GType e_mail_display_popup_extension_get_type (void); void e_mail_display_popup_extension_update_actions (EMailDisplayPopupExtension *extension, - WebKitHitTestResult *context); + const gchar *popup_document_uri); G_END_DECLS diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c index 1e475ca..eed7d74 100644 --- a/mail/e-mail-display.c +++ b/mail/e-mail-display.c @@ -34,20 +34,36 @@ #include <em-format/e-mail-part-attachment.h> #include <em-format/e-mail-part-utils.h> +#include "e-cid-request.h" #include "e-http-request.h" #include "e-mail-display-popup-extension.h" #include "e-mail-notes.h" #include "e-mail-request.h" +#include "e-mail-ui-session.h" #include "em-composer-utils.h" #include "em-utils.h" +#include <web-extensions/e-web-extension-names.h> + #define d(x) #define E_MAIL_DISPLAY_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayPrivate)) +typedef enum { + E_ATTACHMENT_FLAG_VISIBLE = (1 << 0), + E_ATTACHMENT_FLAG_ZOOMED_TO_100 = (1 << 1) +} EAttachmentFlags; + struct _EMailDisplayPrivate { + EAttachmentStore *attachment_store; + EAttachmentView *attachment_view; + GHashTable *attachment_flags; /* EAttachment * ~> guint bit-or of EAttachmentFlags */ + guint attachment_inline_ui_id; + + GtkActionGroup *attachment_inline_group; + EMailPartList *part_list; EMailFormatterMode mode; EMailFormatter *formatter; @@ -58,8 +74,6 @@ struct _EMailDisplayPrivate { GSettings *settings; - GHashTable *widgets; - guint scheduled_reload; GHashTable *old_settings; @@ -67,10 +81,16 @@ struct _EMailDisplayPrivate { GMutex remote_content_lock; EMailRemoteContent *remote_content; GHashTable *skipped_remote_content_sites; + + guint web_extension_headers_collapsed_signal_id; + + GtkAllocation attachment_popup_position; }; enum { PROP_0, + PROP_ATTACHMENT_STORE, + PROP_ATTACHMENT_VIEW, PROP_FORMATTER, PROP_HEADERS_COLLAPSABLE, PROP_HEADERS_COLLAPSED, @@ -142,6 +162,21 @@ G_DEFINE_TYPE ( e_mail_display, E_TYPE_WEB_VIEW); +static const gchar *attachment_popup_ui = +"<ui>" +" <popup name='context'>" +" <placeholder name='inline-actions'>" +" <menuitem action='zoom-to-100'/>" +" <menuitem action='zoom-to-window'/>" +" <menuitem action='show'/>" +" <menuitem action='show-all'/>" +" <separator/>" +" <menuitem action='hide'/>" +" <menuitem action='hide-all'/>" +" </placeholder>" +" </popup>" +"</ui>"; + static void e_mail_display_claim_skipped_uri (EMailDisplay *mail_display, const gchar *uri) @@ -259,37 +294,6 @@ formatter_image_loading_policy_changed_cb (GObject *object, e_mail_display_reload (display); } -static gboolean -mail_display_image_exists_in_cache (const gchar *image_uri) -{ - gchar *filename; - gchar *hash; - gboolean exists = FALSE; - - if (!emd_global_http_cache) - return FALSE; - - hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1); - filename = camel_data_cache_get_filename ( - emd_global_http_cache, "http", hash); - - if (filename != NULL) { - struct stat st; - - exists = g_file_test (filename, G_FILE_TEST_EXISTS); - if (exists && g_stat (filename, &st) == 0) { - exists = st.st_size != 0; - } else { - exists = FALSE; - } - g_free (filename); - } - - g_free (hash); - - return exists; -} - static void mail_display_update_formatter_colors (EMailDisplay *display) { @@ -303,29 +307,6 @@ mail_display_update_formatter_colors (EMailDisplay *display) e_mail_formatter_update_style (formatter, state_flags); } -static void -mail_display_plugin_widget_disconnect_children (GtkWidget *widget, - gpointer mail_display) -{ - g_signal_handlers_disconnect_by_data (widget, mail_display); -} - -static void -mail_display_plugin_widget_disconnect (gpointer widget_uri, - gpointer widget, - gpointer mail_display) -{ - if (E_IS_ATTACHMENT_BAR (widget)) - g_signal_handlers_disconnect_by_data (widget, mail_display); - else if (E_IS_ATTACHMENT_BUTTON (widget)) - g_signal_handlers_disconnect_by_data (widget, mail_display); - else if (GTK_IS_CONTAINER (widget)) - gtk_container_foreach ( - widget, - mail_display_plugin_widget_disconnect_children, - mail_display); -} - static gboolean mail_display_process_mailto (EWebView *web_view, const gchar *mailto_uri, @@ -355,14 +336,28 @@ mail_display_process_mailto (EWebView *web_view, } static gboolean -mail_display_link_clicked (WebKitWebView *web_view, - WebKitWebFrame *frame, - WebKitNetworkRequest *request, - WebKitWebNavigationAction *navigation_action, - WebKitWebPolicyDecision *policy_decision, - gpointer user_data) +decide_policy_cb (WebKitWebView *web_view, + WebKitPolicyDecision *decision, + WebKitPolicyDecisionType type) { - const gchar *uri = webkit_network_request_get_uri (request); + WebKitNavigationPolicyDecision *navigation_decision; + WebKitNavigationAction *navigation_action; + WebKitURIRequest *request; + const gchar *uri; + + if (type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION) + return FALSE; + + navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION (decision); + navigation_action = webkit_navigation_policy_decision_get_navigation_action (navigation_decision); + request = webkit_navigation_action_get_request (navigation_action); + + uri = webkit_uri_request_get_uri (request); + + if (!uri || !*uri) { + webkit_policy_decision_ignore (decision); + return TRUE; + } if (g_str_has_prefix (uri, "file://")) { gchar *filename; @@ -370,8 +365,9 @@ mail_display_link_clicked (WebKitWebView *web_view, filename = g_filename_from_uri (uri, NULL, NULL); if (g_file_test (filename, G_FILE_TEST_IS_DIR)) { - webkit_web_policy_decision_ignore (policy_decision); - webkit_network_request_set_uri (request, "about:blank"); + webkit_policy_decision_ignore (decision); + /* FIXME WK2 Not sure if the request will be changed there */ + webkit_uri_request_set_uri (request, "about:blank"); g_free (filename); return TRUE; } @@ -381,17 +377,17 @@ mail_display_link_clicked (WebKitWebView *web_view, if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) { /* do nothing, function handled the "mailto:" uri already */ - webkit_web_policy_decision_ignore (policy_decision); + webkit_policy_decision_ignore (decision); return TRUE; } else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) { /* ignore */ - webkit_web_policy_decision_ignore (policy_decision); + webkit_policy_decision_ignore (decision); return TRUE; } else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) { /* ignore */ - webkit_web_policy_decision_ignore (policy_decision); + webkit_policy_decision_ignore (decision); return TRUE; } @@ -401,942 +397,808 @@ mail_display_link_clicked (WebKitWebView *web_view, } static void -mail_display_resource_requested (WebKitWebView *web_view, - WebKitWebFrame *frame, - WebKitWebResource *resource, - WebKitNetworkRequest *request, - WebKitNetworkResponse *response, - gpointer user_data) +add_color_css_rule_for_web_view (EWebView *view, + const gchar *color_name, + const gchar *color_value) { - const gchar *original_uri; - - original_uri = webkit_network_request_get_uri (request); + gchar *selector; + gchar *style; - if (original_uri != NULL) { - gchar *redirected_uri; + selector = g_strconcat (".-e-mail-formatter-", color_name, NULL); - redirected_uri = e_web_view_redirect_uri ( - E_WEB_VIEW (web_view), original_uri); + if (g_strstr_len (color_name, -1, "header")) { + style = g_strconcat ( + "color: ", color_value, " !important;", NULL); + } else if (g_strstr_len (color_name, -1, "frame")) { + style = g_strconcat ( + "border-color: ", color_value, NULL); + } else { + style = g_strconcat ( + "background-color: ", color_value, " !important;", NULL); + } - webkit_network_request_set_uri (request, redirected_uri); + e_web_view_add_css_rule_into_style_sheet ( + view, + "-e-mail-formatter-style-sheet", + selector, + style); - g_free (redirected_uri); - } + g_free (style); + g_free (selector); } -static WebKitDOMElement * -find_element_by_id (WebKitDOMDocument *document, - const gchar *id) +static void +initialize_web_view_colors (EMailDisplay *display) { - WebKitDOMNodeList *frames; - WebKitDOMElement *element = NULL; - gulong ii, length; - - if (!WEBKIT_DOM_IS_DOCUMENT (document)) - return NULL; + EMailFormatter *formatter; + GtkTextDirection direction; + const gchar *style; + gint ii; - /* Try to look up the element in this DOM document */ - element = webkit_dom_document_get_element_by_id (document, id); - if (element != NULL) - return element; + const gchar *color_names[] = { + "body-color", + "citation-color", + "frame-color", + "header-color", + NULL + }; - /* If the element is not here then recursively scan all frames */ - frames = webkit_dom_document_get_elements_by_tag_name ( - document, "iframe"); - length = webkit_dom_node_list_get_length (frames); - for (ii = 0; ii < length; ii++) { - WebKitDOMHTMLIFrameElement *iframe; - WebKitDOMDocument *frame_doc; + formatter = e_mail_display_get_formatter (display); - iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT ( - webkit_dom_node_list_item (frames, ii)); + for (ii = 0; color_names[ii]; ii++) { + GdkRGBA *color = NULL; + gchar *color_value; - frame_doc = webkit_dom_html_iframe_element_get_content_document (iframe); + g_object_get (formatter, color_names[ii], &color, NULL); + color_value = g_strdup_printf ("#%06x", e_rgba_to_value (color)); - element = find_element_by_id (frame_doc, id); + add_color_css_rule_for_web_view ( + E_WEB_VIEW (display), + color_names[ii], + color_value); - g_object_unref (iframe); - if (element != NULL) - break; + gdk_rgba_free (color); + g_free (color_value); } - g_object_unref (frames); - - return element; -} + e_web_view_add_css_rule_into_style_sheet ( + E_WEB_VIEW (display), + "-e-mail-formatter-style-sheet", + ".-e-mail-formatter-frame-security-none", + "border-width: 1px; border-style: solid"); -static void -mail_display_plugin_widget_resize (GtkWidget *widget, - gpointer dummy, - EMailDisplay *display) -{ - WebKitDOMElement *parent_element; - gchar *dim; - gint height, width; - gfloat scale; - - parent_element = g_object_get_data ( - G_OBJECT (widget), "parent_element"); - - if (!WEBKIT_DOM_IS_ELEMENT (parent_element)) { - d ( - printf ("%s: %s does not have (valid) parent element!\n", - G_STRFUNC, (gchar *) g_object_get_data (G_OBJECT (widget), "uri"))); - return; - } + /* the rgba values below were copied from e-formatter-secure-button */ + direction = gtk_widget_get_default_direction (); - scale = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (display)); - width = gtk_widget_get_allocated_width (widget); - gtk_widget_get_preferred_height_for_width (widget, width, &height, NULL); + if (direction == GTK_TEXT_DIR_RTL) + style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(53%, 73%, 53%, 1.0)"; + else + style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(53%, 73%, 53%, 1.0)"; + e_web_view_add_css_rule_into_style_sheet ( + E_WEB_VIEW (display), + "-e-mail-formatter-style-sheet", + ".-e-mail-formatter-frame-security-good", + style); - /* When zooming WebKit does not change dimensions of the elements, - * but only scales them on the canvas. GtkWidget can't be scaled - * though so we need to cope with the dimension changes to keep the - * the widgets the correct size. Due to inaccuracy in rounding - * (float -> int) it still acts a bit funny, but at least it does - * not cause widgets in WebKit to go crazy when zooming. */ - height = height * (1 / scale); + if (direction == GTK_TEXT_DIR_RTL) + style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(73%, 53%, 53%, 1.0)"; + else + style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(73%, 53%, 53%, 1.0)"; + e_web_view_add_css_rule_into_style_sheet ( + E_WEB_VIEW (display), + "-e-mail-formatter-style-sheet", + ".-e-mail-formatter-frame-security-bad", + style); - /* Int -> Str */ - dim = g_strdup_printf ("%d", height); + if (direction == GTK_TEXT_DIR_RTL) + style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)"; + else + style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)"; + e_web_view_add_css_rule_into_style_sheet ( + E_WEB_VIEW (display), + "-e-mail-formatter-style-sheet", + ".-e-mail-formatter-frame-security-unknown", + style); - /* Set height of the containment <object> to match height of the - * GtkWidget it contains */ - webkit_dom_html_object_element_set_height ( - WEBKIT_DOM_HTML_OBJECT_ELEMENT (parent_element), dim); - g_free (dim); + if (direction == GTK_TEXT_DIR_RTL) + style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)"; + else + style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)"; + e_web_view_add_css_rule_into_style_sheet ( + E_WEB_VIEW (display), + "-e-mail-formatter-style-sheet", + ".-e-mail-formatter-frame-security-need-key", + style); } static void -plugin_widget_set_parent_element (GtkWidget *widget, - EMailDisplay *display) +headers_collapsed_signal_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + EMailDisplay *display) { - const gchar *uri; - WebKitDOMDocument *document; - WebKitDOMElement *element; + gboolean expanded; - uri = g_object_get_data (G_OBJECT (widget), "uri"); - if (uri == NULL || *uri == '\0') + if (g_strcmp0 (signal_name, "HeadersCollapsed") != 0) return; - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display)); - element = find_element_by_id (document, uri); + if (parameters) + g_variant_get (parameters, "(b)", &expanded); - if (!WEBKIT_DOM_IS_ELEMENT (element)) { - g_warning ("Failed to find parent <object> for '%s' - no ID set?", uri); - return; - } + e_mail_display_set_headers_collapsed (display, expanded); +} - /* Assign the WebKitDOMElement to "parent_element" data of the - * GtkWidget and the GtkWidget to "widget" data of the DOM Element. */ - g_object_set_data (G_OBJECT (widget), "parent_element", element); - g_object_set_data (G_OBJECT (element), "widget", widget); +static void +setup_dom_bindings (EMailDisplay *display) +{ + GDBusProxy *web_extension; + + web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (display)); + + if (web_extension) { + if (display->priv->web_extension_headers_collapsed_signal_id == 0) { + display->priv->web_extension_headers_collapsed_signal_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (web_extension), + g_dbus_proxy_get_name (web_extension), + E_WEB_EXTENSION_INTERFACE, + "HeadersCollapsed", + E_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback) headers_collapsed_signal_cb, + display, + NULL); + } - e_binding_bind_property ( - element, "hidden", - widget, "visible", - G_BINDING_SYNC_CREATE | - G_BINDING_INVERT_BOOLEAN); + g_dbus_proxy_call ( + web_extension, + "EMailDisplayBindDOM", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (display))), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } } static void -attachment_button_expanded (GObject *object, - GParamSpec *pspec, - gpointer user_data) +mail_display_change_one_attachment_visibility (EMailDisplay *display, + EAttachment *attachment, + gboolean show, + gboolean flip) { - EAttachmentButton *button = E_ATTACHMENT_BUTTON (object); - EMailDisplay *display = user_data; - WebKitDOMDocument *document; - WebKitDOMElement *element, *iframe; - WebKitDOMCSSStyleDeclaration *css; - const gchar *attachment_part_id; gchar *element_id; - gboolean expanded; - - d ( - printf ("Attachment button %s has been %s!\n", - (gchar *) g_object_get_data (object, "uri"), - (e_attachment_button_get_expanded (button) ? "expanded" : "collapsed"))); - - expanded = - e_attachment_button_get_expanded (button) && - gtk_widget_get_visible (GTK_WIDGET (button)); + gchar *uri; + guint flags; - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display)); - attachment_part_id = g_object_get_data (object, "attachment_id"); + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (g_hash_table_contains (display->priv->attachment_flags, attachment)); - element_id = g_strconcat (attachment_part_id, ".wrapper", NULL); - element = find_element_by_id (document, element_id); - g_free (element_id); + flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment)); + if (flip) + show = !(flags & E_ATTACHMENT_FLAG_VISIBLE); - if (!WEBKIT_DOM_IS_ELEMENT (element)) { - d ( - printf ("%s: Content <div> of attachment %s does not exist!!\n", - G_STRFUNC, (gchar *) g_object_get_data (object, "uri"))); + if ((((flags & E_ATTACHMENT_FLAG_VISIBLE) != 0) ? 1 : 0) == (show ? 1 : 0)) return; - } - - if (WEBKIT_DOM_IS_HTML_ELEMENT (element) && expanded && - webkit_dom_element_get_child_element_count (element) == 0) { - gchar *inner_html_data; - inner_html_data = webkit_dom_element_get_attribute (element, "inner-html-data"); - if (inner_html_data && *inner_html_data) { - WebKitDOMHTMLElement *html_element; + if (show) + flags = flags | E_ATTACHMENT_FLAG_VISIBLE; + else + flags = flags & (~E_ATTACHMENT_FLAG_VISIBLE); + g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags)); - html_element = WEBKIT_DOM_HTML_ELEMENT (element); - webkit_dom_html_element_set_inner_html (html_element, inner_html_data, NULL); + element_id = g_strdup_printf ("attachment-wrapper-%p", attachment); + e_web_view_set_element_hidden (E_WEB_VIEW (display), element_id, !show); + g_free (element_id); - webkit_dom_element_remove_attribute (element, "inner-html-data"); - } + element_id = g_strdup_printf ("attachment-expander-img-%p", attachment); + uri = g_strdup_printf ("gtk-stock://%s?size=%d", show ? "go-down" : "go-next", GTK_ICON_SIZE_BUTTON); - g_free (inner_html_data); - } + e_web_view_set_element_attribute (E_WEB_VIEW (display), element_id, NULL, "src", uri); - /* Hide/Show all the GtkWidgets inside an attachment, otherwise they could - * be visible even if the wrapper is hidden. */ - if ((iframe = webkit_dom_element_query_selector (element, "iframe", NULL))) { - WebKitDOMDocument *content_document; + g_free (element_id); + g_free (uri); +} - content_document = webkit_dom_html_iframe_element_get_content_document - (WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)); +static void +mail_display_change_attachment_visibility (EMailDisplay *display, + gboolean all, + gboolean show) +{ + EAttachmentView *view; + GList *attachments, *link; - if (content_document) { - gint length, ii; - WebKitDOMNodeList *list; + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - list = webkit_dom_document_get_elements_by_tag_name (content_document, "object"); - length = webkit_dom_node_list_get_length (list); + view = e_mail_display_get_attachment_view (display); + g_return_if_fail (view != NULL); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *item; + if (all) + attachments = e_attachment_store_get_attachments (display->priv->attachment_store); + else + attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL; - item = webkit_dom_node_list_item (list, ii); + for (link = attachments; link; link = g_list_next (link)) { + EAttachment *attachment = link->data; - css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (item)); - if (expanded) - g_free (webkit_dom_css_style_declaration_remove_property (css, "display", NULL)); - else - webkit_dom_css_style_declaration_set_property (css, "display", "none", "", NULL); - g_clear_object (&css); - } - g_object_unref (list); - } + if (e_attachment_get_can_show (attachment)) + mail_display_change_one_attachment_visibility (display, attachment, show, FALSE); } - /* Show or hide the DIV which contains - * the attachment (iframe, image...). */ - css = webkit_dom_element_get_style (element); - webkit_dom_css_style_declaration_set_property ( - css, "display", expanded ? "block" : "none", "", NULL); - g_object_unref (css); + g_list_free_full (attachments, g_object_unref); } static void -attachment_button_zoom_to_window_cb (GObject *object, - GParamSpec *pspec, - gpointer user_data) +mail_attachment_change_zoom (EMailDisplay *display, + gboolean to_100_percent) { - EAttachmentButton *button = E_ATTACHMENT_BUTTON (object); - EMailDisplay *display = user_data; - WebKitDOMDocument *document; - WebKitDOMElement *element, *child; - WebKitDOMCSSStyleDeclaration *css; - const gchar *attachment_part_id; - gchar *element_id; - gboolean zoom_to_window; + EAttachmentView *view; + GList *attachments, *link; - d ( - printf ("Attachment button %s has been set to %s!\n", - (gchar *) g_object_get_data (object, "uri"), - (e_attachment_botton_get_zoom_to_window (attachment) ? "zoom-to-window" : "zoom to 100%"))); - - if (!gtk_widget_get_visible (GTK_WIDGET (button))) - return; + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - zoom_to_window = e_attachment_button_get_zoom_to_window (button); + view = e_mail_display_get_attachment_view (display); + g_return_if_fail (view != NULL); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display)); - attachment_part_id = g_object_get_data (object, "attachment_id"); + attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL; - element_id = g_strconcat (attachment_part_id, ".wrapper", NULL); - element = find_element_by_id (document, element_id); - g_free (element_id); - - if (!WEBKIT_DOM_IS_ELEMENT (element)) { - d ( - printf ("%s: Content <div> of attachment %s does not exist!!\n", - G_STRFUNC, (gchar *) g_object_get_data (object, "uri"))); - return; - } + for (link = attachments; link; link = g_list_next (link)) { + EAttachment *attachment = link->data; + gchar *element_id; + const gchar *max_width; + guint flags; - child = webkit_dom_element_get_first_element_child (element); - if (!child || !WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (child)) { - d ( - printf ("%s: Content <div> of attachment %s does not contain image, but %s\n", - G_STRFUNC, (gchar *) g_object_get_data (object, "uri"), - child ? G_OBJECT_TYPE_NAME (child) : "[null]")); - g_clear_object (&child); - return; - } + if (!E_IS_ATTACHMENT (attachment) || + !g_hash_table_contains (display->priv->attachment_flags, attachment)) + continue; - css = webkit_dom_element_get_style (child); - if (zoom_to_window) { - webkit_dom_css_style_declaration_set_property (css, "max-width", "100%", "", NULL); - } else { - g_free (webkit_dom_css_style_declaration_remove_property (css, "max-width", NULL)); - } - g_object_unref (css); - g_clear_object (&child); -} + flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment)); + if ((((flags & E_ATTACHMENT_FLAG_ZOOMED_TO_100) != 0) ? 1 : 0) == (to_100_percent ? 1 : 0)) + continue; -static void -mail_display_attachment_count_changed (EAttachmentStore *store, - GParamSpec *pspec, - GtkWidget *box) -{ - WebKitDOMHTMLElement *element; - GList *children; + if (to_100_percent) + flags = flags | E_ATTACHMENT_FLAG_ZOOMED_TO_100; + else + flags = flags & (~E_ATTACHMENT_FLAG_ZOOMED_TO_100); + g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags)); - children = gtk_container_get_children (GTK_CONTAINER (box)); - g_return_if_fail (children && children->data); + if (to_100_percent) + max_width = NULL; + else + max_width = "100%"; - element = g_object_get_data (children->data, "parent_element"); - g_list_free (children); + element_id = g_strdup_printf ("attachment-wrapper-%p::child", attachment); - g_return_if_fail (WEBKIT_DOM_IS_HTML_ELEMENT (element)); + e_web_view_set_element_style_property (E_WEB_VIEW (display), element_id, "max-width", max_width, ""); - if (e_attachment_store_get_num_attachments (store) == 0) { - gtk_widget_hide (box); - webkit_dom_html_element_set_hidden (element, TRUE); - } else { - gtk_widget_show (box); - webkit_dom_html_element_set_hidden (element, FALSE); + g_free (element_id); } -} -typedef struct _NumAttachmentsData { - EAttachmentStore *store; - gulong handler_id; -} NumAttachmentsData; + g_list_free_full (attachments, g_object_unref); +} static void -attachment_bar_box_gone_cb (gpointer data, - GObject *gone_box) +action_attachment_show_cb (GtkAction *action, + EMailDisplay *display) { - NumAttachmentsData *nad = data; + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - if (nad) { - g_signal_handler_disconnect (nad->store, nad->handler_id); - g_object_unref (nad->store); - g_free (nad); - } + mail_display_change_attachment_visibility (display, FALSE, TRUE); } -static GtkWidget * -mail_display_plugin_widget_requested (WebKitWebView *web_view, - gchar *mime_type, - gchar *uri, - GHashTable *param, - gpointer user_data) +static void +action_attachment_show_all_cb (GtkAction *action, + EMailDisplay *display) { - EMailDisplay *display; - EMailExtensionRegistry *reg; - EMailFormatterExtension *extension; - GQueue *extensions; - GList *head, *link; - EMailPart *part = NULL; - GtkWidget *widget = NULL; - GWeakRef *weakref; - gchar *part_id, *type, *object_uri; + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - part_id = g_hash_table_lookup (param, "data"); - if (part_id == NULL || !g_str_has_prefix (uri, "mail://")) - return NULL; + mail_display_change_attachment_visibility (display, TRUE, TRUE); +} - type = g_hash_table_lookup (param, "type"); - if (type == NULL) - return NULL; +static void +action_attachment_hide_cb (GtkAction *action, + EMailDisplay *display) +{ + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - display = E_MAIL_DISPLAY (web_view); + mail_display_change_attachment_visibility (display, FALSE, FALSE); +} - weakref = g_hash_table_lookup (display->priv->widgets, part_id); - if (weakref) - widget = g_weak_ref_get (weakref); +static void +action_attachment_hide_all_cb (GtkAction *action, + EMailDisplay *display) +{ + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - if (widget != NULL) { - /* This cannot be the last reference; thread-safety is assured, - because this runs in the main thread only. */ - g_object_unref (widget); - d (printf ("Handled %s widget request from cache\n", part_id)); - return widget; - } + mail_display_change_attachment_visibility (display, TRUE, FALSE); +} - /* Find the EMailPart representing the requested widget. */ - part = e_mail_part_list_ref_part (display->priv->part_list, part_id); - if (part == NULL) - return NULL; +static void +action_attachment_zoom_to_100_cb (GtkAction *action, + EMailDisplay *display) +{ + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - reg = e_mail_formatter_get_extension_registry (display->priv->formatter); - extensions = e_mail_extension_registry_get_for_mime_type (reg, type); - if (extensions == NULL) - goto exit; + mail_attachment_change_zoom (display, TRUE); +} - extension = NULL; - head = g_queue_peek_head_link (extensions); - for (link = head; link != NULL; link = g_list_next (link)) { - extension = link->data; +static void +action_attachment_zoom_to_window_cb (GtkAction *action, + EMailDisplay *display) +{ + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - if (extension == NULL) - continue; + mail_attachment_change_zoom (display, FALSE); +} - if (e_mail_formatter_extension_has_widget (extension)) - break; - } +static GtkActionEntry attachment_inline_entries[] = { - if (extension == NULL) - goto exit; - - /* Get the widget from formatter */ - widget = e_mail_formatter_extension_get_widget ( - extension, display->priv->part_list, part, param); - d ( - printf ("Created widget %s (%p) for part %s\n", - G_OBJECT_TYPE_NAME (widget), widget, part_id)); - - /* Should not happen! WebKit will display an ugly 'Plug-in not - * available' placeholder instead of hiding the <object> element. */ - if (widget == NULL) - goto exit; - - /* Attachment button has URI different then the actual PURI because - * that URI identifies the attachment itself */ - if (E_IS_ATTACHMENT_BUTTON (widget)) { - EMailPartAttachment *empa = (EMailPartAttachment *) part; - EAttachment *attachment; - gchar *attachment_part_id; + { "hide", + NULL, + N_("_Hide"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_attachment_hide_cb) }, - if (empa->attachment_view_part_id) - attachment_part_id = empa->attachment_view_part_id; - else - attachment_part_id = part_id; + { "hide-all", + NULL, + N_("Hid_e All"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_attachment_hide_all_cb) }, - object_uri = g_strconcat ( - attachment_part_id, ".attachment_button", NULL); - g_object_set_data_full ( - G_OBJECT (widget), "attachment_id", - g_strdup (attachment_part_id), - (GDestroyNotify) g_free); + { "show", + NULL, + N_("_View Inline"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_attachment_show_cb) }, - attachment = e_mail_part_attachment_ref_attachment (empa); - if (attachment && e_attachment_is_mail_note (attachment)) { - CamelFolder *folder; - const gchar *message_uid; + { "show-all", + NULL, + N_("Vie_w All Inline"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_attachment_show_all_cb) }, - folder = e_mail_part_list_get_folder (display->priv->part_list); - message_uid = e_mail_part_list_get_message_uid (display->priv->part_list); + { "zoom-to-100", + NULL, + N_("_Zoom to 100%"), + NULL, + N_("Zoom the image to its natural size"), + G_CALLBACK (action_attachment_zoom_to_100_cb) }, - if (folder && message_uid) { - CamelMessageInfo *info; + { "zoom-to-window", + NULL, + N_("_Zoom to window"), + NULL, + N_("Zoom large images to not be wider than the window width"), + G_CALLBACK (action_attachment_zoom_to_window_cb) } +}; - info = camel_folder_get_message_info (folder, message_uid); - if (info) { - if (!camel_message_info_get_user_flag (info, E_MAIL_NOTES_USER_FLAG)) - camel_message_info_set_user_flag (info, E_MAIL_NOTES_USER_FLAG, TRUE); - camel_message_info_unref (info); - } - } - } +static EAttachment * +mail_display_ref_attachment_from_element (EMailDisplay *display, + const gchar *element_value) +{ + EAttachment *attachment = NULL; + GQueue queue = G_QUEUE_INIT; + GList *head, *link; - g_clear_object (&attachment); - } else { - object_uri = g_strdup (part_id); - } + g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL); + g_return_val_if_fail (element_value != NULL, NULL); - /* Store the uri as data of the widget */ - g_object_set_data_full ( - G_OBJECT (widget), "uri", - object_uri, (GDestroyNotify) g_free); + e_mail_part_list_queue_parts (display->priv->part_list, NULL, &queue); + head = g_queue_peek_head_link (&queue); - /* Set pointer to the <object> element as GObject data - * "parent_element" and set pointer to the widget as GObject - * data "widget" to the <object> element. */ - plugin_widget_set_parent_element (widget, display); + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *part = E_MAIL_PART (link->data); - /* Resizing a GtkWidget requires changing size of parent - * <object> HTML element in DOM. */ - g_signal_connect ( - widget, "size-allocate", - G_CALLBACK (mail_display_plugin_widget_resize), display); + if (E_IS_MAIL_PART_ATTACHMENT (part)) { + EAttachment *adept; + gboolean can_use; + gchar *tmp; - if (E_IS_ATTACHMENT_BAR (widget)) { - GtkWidget *box = NULL; - EAttachmentStore *store; - NumAttachmentsData *nad; + adept = e_mail_part_attachment_ref_attachment (E_MAIL_PART_ATTACHMENT (part)); - /* Only when packed in box (grid does not work), - * EAttachmentBar reports correct height */ - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0); + tmp = g_strdup_printf ("%p", adept); + can_use = g_strcmp0 (tmp, element_value) == 0; + g_free (tmp); - /* When EAttachmentBar is expanded/collapsed it does not - * emit size-allocate signal despite it changes it's height. */ - g_signal_connect ( - widget, "notify::expanded", - G_CALLBACK (mail_display_plugin_widget_resize), - display); - g_signal_connect ( - widget, "notify::active-view", - G_CALLBACK (mail_display_plugin_widget_resize), - display); - - /* Always hide an attachment bar without attachments */ - store = e_attachment_bar_get_store (E_ATTACHMENT_BAR (widget)); - - nad = g_new0 (NumAttachmentsData, 1); - nad->store = g_object_ref (store); - nad->handler_id = g_signal_connect ( - store, "notify::num-attachments", - G_CALLBACK (mail_display_attachment_count_changed), - box); - - g_object_weak_ref (G_OBJECT (box), attachment_bar_box_gone_cb, nad); - - gtk_widget_show (widget); - gtk_widget_show (box); - - /* Initial sync */ - mail_display_attachment_count_changed (store, NULL, box); - - widget = box; - - } else if (E_IS_ATTACHMENT_BUTTON (widget)) { - - /* Bind visibility of DOM element containing related - * attachment with 'expanded' property of this - * attachment button. */ - EMailPartAttachment *empa = (EMailPartAttachment *) part; - - e_attachment_button_set_expandable (E_ATTACHMENT_BUTTON (widget), - e_mail_part_attachment_get_expandable (empa)); - - if (e_mail_part_attachment_get_expandable (empa)) { - /* Show/hide the attachment when the EAttachmentButton - * is expanded/collapsed or shown/hidden. */ - g_signal_connect ( - widget, "notify::expanded", - G_CALLBACK (attachment_button_expanded), - display); - g_signal_connect ( - widget, "notify::visible", - G_CALLBACK (attachment_button_expanded), - display); - g_signal_connect ( - widget, "notify::zoom-to-window", - G_CALLBACK (attachment_button_zoom_to_window_cb), - display); - - if (e_mail_part_should_show_inline (part)) { - e_attachment_button_set_expanded ( - E_ATTACHMENT_BUTTON (widget), TRUE); - } else { - e_attachment_button_set_expanded ( - E_ATTACHMENT_BUTTON (widget), FALSE); - attachment_button_expanded ( - G_OBJECT (widget), NULL, display); + if (can_use) { + attachment = adept; + break; } + + g_clear_object (&adept); } } - g_hash_table_insert ( - display->priv->widgets, - g_strdup (object_uri), e_weak_ref_new (widget)); - -exit: - if (part != NULL) - g_object_unref (part); + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); - return widget; + return attachment; } static void -toggle_headers_visibility (WebKitDOMElement *button, - WebKitDOMEvent *event, - WebKitWebView *web_view) +mail_display_attachment_expander_clicked_cb (EWebView *web_view, + const gchar *element_class, + const gchar *element_value, + const GtkAllocation *element_position, + gpointer user_data) { - WebKitDOMDocument *document; - WebKitDOMElement *short_headers = NULL, *full_headers = NULL; - WebKitDOMCSSStyleDeclaration *css_short = NULL, *css_full = NULL; - gboolean expanded; - const gchar *path; - gchar *css_value; + EMailDisplay *display; + EAttachment *attachment; - document = webkit_web_view_get_dom_document (web_view); + g_return_if_fail (E_IS_MAIL_DISPLAY (web_view)); + g_return_if_fail (element_class != NULL); + g_return_if_fail (element_value != NULL); + g_return_if_fail (element_position != NULL); - short_headers = webkit_dom_document_get_element_by_id ( - document, "__evo-short-headers"); - if (short_headers == NULL) - return; - - css_short = webkit_dom_element_get_style (short_headers); + display = E_MAIL_DISPLAY (web_view); + attachment = mail_display_ref_attachment_from_element (display, element_value); - full_headers = webkit_dom_document_get_element_by_id ( - document, "__evo-full-headers"); - if (full_headers == NULL) - goto clean; + if (attachment) { + /* Flip the current 'visible' state */ + mail_display_change_one_attachment_visibility (display, attachment, FALSE, TRUE); + } - css_full = webkit_dom_element_get_style (full_headers); - css_value = webkit_dom_css_style_declaration_get_property_value ( - css_full, "display"); - expanded = (g_strcmp0 (css_value, "table") == 0); - g_free (css_value); + g_clear_object (&attachment); +} - webkit_dom_css_style_declaration_set_property ( - css_full, "display", - expanded ? "none" : "table", "", NULL); - webkit_dom_css_style_declaration_set_property ( - css_short, "display", - expanded ? "table" : "none", "", NULL); +static void +mail_display_attachment_inline_update_actions (EMailDisplay *display) +{ + GtkActionGroup *action_group; + GtkAction *action; + GList *attachments, *link; + EAttachmentView *view; + guint n_shown = 0; + guint n_hidden = 0; + guint n_selected = 0; + gboolean can_show = FALSE; + gboolean shown = FALSE; + gboolean is_image = FALSE; + gboolean zoomed_to_100 = FALSE; + gboolean visible; - if (expanded) - path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png"; - else - path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png"; + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - webkit_dom_html_image_element_set_src ( - WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path); + action_group = display->priv->attachment_inline_group; + g_return_if_fail (action_group != NULL); - e_mail_display_set_headers_collapsed ( - E_MAIL_DISPLAY (web_view), expanded); + attachments = e_attachment_store_get_attachments (display->priv->attachment_store); - d (printf ("Headers %s!\n", expanded ? "collapsed" : "expanded")); - clean: - g_clear_object (&short_headers); - g_clear_object (&css_short); - g_clear_object (&full_headers); - g_clear_object (&css_full); -} + for (link = attachments; link; link = g_list_next (link)) { + EAttachment *attachment = link->data; + guint32 flags; -static void -toggle_address_visibility (WebKitDOMElement *button, - WebKitDOMEvent *event) -{ - WebKitDOMElement *full_addr = NULL, *ellipsis = NULL; - WebKitDOMElement *parent = NULL, *bold = NULL; - WebKitDOMCSSStyleDeclaration *css_full = NULL, *css_ellipsis = NULL; - const gchar *path; - gchar *property_value; - gboolean expanded; + if (!e_attachment_get_can_show (attachment)) + continue; - /* <b> element */ - bold = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (button)); - /* <td> element */ - parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (bold)); - g_object_unref (bold); + flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment)); + if ((flags & E_ATTACHMENT_FLAG_VISIBLE) != 0) + n_shown++; + else + n_hidden++; + } - full_addr = webkit_dom_element_query_selector (parent, "#__evo-moreaddr", NULL); + g_list_free_full (attachments, g_object_unref); - if (!full_addr) - goto clean; + view = e_mail_display_get_attachment_view (display); + attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL; + n_selected = g_list_length (attachments); - css_full = webkit_dom_element_get_style (full_addr); + if (n_selected == 1) { + EAttachment *attachment; + gchar *mime_type; + guint32 flags; - ellipsis = webkit_dom_element_query_selector (parent, "#__evo-moreaddr-ellipsis", NULL); + attachment = attachments->data; + mime_type = e_attachment_dup_mime_type (attachment); + can_show = e_attachment_get_can_show (attachment); + is_image = can_show && mime_type && g_ascii_strncasecmp (mime_type, "image/", 6) == 0; - if (!ellipsis) - goto clean; + flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment)); + shown = (flags & E_ATTACHMENT_FLAG_VISIBLE) != 0; + zoomed_to_100 = (flags & E_ATTACHMENT_FLAG_ZOOMED_TO_100) != 0; - css_ellipsis = webkit_dom_element_get_style (ellipsis); + g_free (mime_type); + } + g_list_free_full (attachments, g_object_unref); - property_value = webkit_dom_css_style_declaration_get_property_value (css_full, "display"); - expanded = g_strcmp0 (property_value, "inline") == 0; - g_free (property_value); + action = gtk_action_group_get_action (action_group, "show"); + gtk_action_set_visible (action, can_show && !shown); - webkit_dom_css_style_declaration_set_property ( - css_full, "display", (expanded ? "none" : "inline"), "", NULL); - webkit_dom_css_style_declaration_set_property ( - css_ellipsis, "display", (expanded ? "inline" : "none"), "", NULL); + /* Show this action if there are multiple viewable + * attachments, and at least one of them is hidden. */ + visible = (n_shown + n_hidden > 1) && (n_hidden > 0); + action = gtk_action_group_get_action (action_group, "show-all"); + gtk_action_set_visible (action, visible); - if (expanded) - path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png"; - else - path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png"; + action = gtk_action_group_get_action (action_group, "hide"); + gtk_action_set_visible (action, can_show && shown); - if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (button)) { - WebKitDOMElement *element; + /* Show this action if there are multiple viewable + * attachments, and at least one of them is shown. */ + visible = (n_shown + n_hidden > 1) && (n_shown > 0); + action = gtk_action_group_get_action (action_group, "hide-all"); + gtk_action_set_visible (action, visible); - element = webkit_dom_element_query_selector (parent, "#__evo-moreaddr-img", NULL); - if (!element) - goto clean; + action = gtk_action_group_get_action (action_group, "zoom-to-100"); + gtk_action_set_visible (action, can_show && shown && is_image && !zoomed_to_100); - webkit_dom_html_image_element_set_src (WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), path); - - g_object_unref (element); - } else - webkit_dom_html_image_element_set_src (WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path); - clean: - g_clear_object (&css_full); - g_clear_object (&css_ellipsis); - g_clear_object (&full_addr); - g_clear_object (&ellipsis); - g_clear_object (&parent); + action = gtk_action_group_get_action (action_group, "zoom-to-window"); + gtk_action_set_visible (action, can_show && shown && is_image && zoomed_to_100); } static void -add_color_css_rule_for_web_view (EWebView *view, - const gchar *color_name, - const gchar *color_value) +mail_display_attachment_menu_deactivate_cb (GtkMenuShell *menu, + gpointer user_data) { - gchar *selector; - gchar *style; + EMailDisplay *display = user_data; - selector = g_strconcat (".-e-mail-formatter-", color_name, NULL); + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - if (g_strstr_len (color_name, -1, "header")) { - style = g_strconcat ( - "color: ", color_value, " !important;", NULL); - } else if (g_strstr_len (color_name, -1, "frame")) { - style = g_strconcat ( - "border-color: ", color_value, NULL); - } else { - style = g_strconcat ( - "background-color: ", color_value, " !important;", NULL); - } + gtk_action_group_set_visible (display->priv->attachment_inline_group, FALSE); - e_web_view_add_css_rule_into_style_sheet ( - view, - "-e-mail-formatter-style-sheet", - selector, - style); - - g_free (style); - g_free (selector); + g_signal_handlers_disconnect_by_func (menu, + G_CALLBACK (mail_display_attachment_menu_deactivate_cb), display); } static void -initialize_web_view_colors (EMailDisplay *display) +mail_display_attachment_menu_position_cb (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data) { - EMailFormatter *formatter; + GtkRequisition menu_requisition; GtkTextDirection direction; - const gchar *style; - gint ii; + GtkAllocation allocation; + GdkRectangle monitor; + GdkScreen *screen; + GdkWindow *window; + GtkWidget *widget; + EMailDisplay *display = user_data; + gint monitor_num; - const gchar *color_names[] = { - "body-color", - "citation-color", - "frame-color", - "header-color", - NULL - }; + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - formatter = e_mail_display_get_formatter (display); + widget = GTK_WIDGET (display); + gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_requisition, NULL); - for (ii = 0; color_names[ii]; ii++) { - GdkRGBA *color = NULL; - gchar *color_value; + window = gtk_widget_get_parent_window (widget); + screen = gtk_widget_get_screen (GTK_WIDGET (menu)); + monitor_num = gdk_screen_get_monitor_at_window (screen, window); + if (monitor_num < 0) + monitor_num = 0; + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); - g_object_get (formatter, color_names[ii], &color, NULL); - color_value = g_strdup_printf ("#%06x", e_rgba_to_value (color)); + allocation = display->priv->attachment_popup_position; - add_color_css_rule_for_web_view ( - E_WEB_VIEW (display), - color_names[ii], - color_value); + gdk_window_get_origin (window, x, y); + *x += allocation.x; + *y += allocation.y + allocation.height; - gdk_rgba_free (color); - g_free (color_value); - } + direction = gtk_widget_get_direction (widget); + if (direction == GTK_TEXT_DIR_LTR) + *x += MAX (allocation.width - menu_requisition.width, 0); + else if (menu_requisition.width > allocation.width) + *x -= menu_requisition.width - allocation.width; - e_web_view_add_css_rule_into_style_sheet ( - E_WEB_VIEW (display), - "-e-mail-formatter-style-sheet", - ".-e-mail-formatter-frame-security-none", - "border-width: 1px; border-style: solid"); + *push_in = FALSE; +} - /* the rgba values below were copied from e-formatter-secure-button */ - direction = gtk_widget_get_default_direction (); +static void +mail_display_attachment_select_path (EAttachmentView *view, + EAttachment *attachment) +{ + GtkTreePath *path; + GtkTreeIter iter; + EAttachmentStore *store; - if (direction == GTK_TEXT_DIR_RTL) - style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(53%, 73%, 53%, 1.0)"; - else - style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(53%, 73%, 53%, 1.0)"; - e_web_view_add_css_rule_into_style_sheet ( - E_WEB_VIEW (display), - "-e-mail-formatter-style-sheet", - ".-e-mail-formatter-frame-security-good", - style); + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); - if (direction == GTK_TEXT_DIR_RTL) - style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(73%, 53%, 53%, 1.0)"; - else - style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(73%, 53%, 53%, 1.0)"; - e_web_view_add_css_rule_into_style_sheet ( - E_WEB_VIEW (display), - "-e-mail-formatter-style-sheet", - ".-e-mail-formatter-frame-security-bad", - style); + store = e_attachment_view_get_store (view); + g_return_if_fail (e_attachment_store_find_attachment_iter (store, attachment, &iter)); - if (direction == GTK_TEXT_DIR_RTL) - style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)"; - else - style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)"; - e_web_view_add_css_rule_into_style_sheet ( - E_WEB_VIEW (display), - "-e-mail-formatter-style-sheet", - ".-e-mail-formatter-frame-security-unknown", - style); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); - if (direction == GTK_TEXT_DIR_RTL) - style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)"; - else - style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)"; - e_web_view_add_css_rule_into_style_sheet ( - E_WEB_VIEW (display), - "-e-mail-formatter-style-sheet", - ".-e-mail-formatter-frame-security-need-key", - style); + e_attachment_view_unselect_all (view); + e_attachment_view_select_path (view, path); + + gtk_tree_path_free (path); } static void -setup_image_click_event_listeners_on_document (WebKitDOMDocument *document, - WebKitWebView *web_view) +mail_display_attachment_menu_clicked_cb (EWebView *web_view, + const gchar *element_class, + const gchar *element_value, + const GtkAllocation *element_position, + gpointer user_data) { - gint length, ii = 0; - WebKitDOMElement *button; - WebKitDOMNodeList *list; + EMailDisplay *display; + EAttachmentView *view; + EAttachment *attachment; + + g_return_if_fail (E_IS_MAIL_DISPLAY (web_view)); + g_return_if_fail (element_class != NULL); + g_return_if_fail (element_value != NULL); + g_return_if_fail (element_position != NULL); - /* Install event listeners on document */ - button = webkit_dom_document_get_element_by_id ( - document, "__evo-collapse-headers-img"); - if (button != NULL) - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (button), "click", - G_CALLBACK (toggle_headers_visibility), - FALSE, web_view); + display = E_MAIL_DISPLAY (web_view); + view = e_mail_display_get_attachment_view (display); + attachment = mail_display_ref_attachment_from_element (display, element_value); - list = webkit_dom_document_query_selector_all (document, "*[id^=__evo-moreaddr-]", NULL); + if (view && attachment) { + GtkWidget *popup_menu; - length = webkit_dom_node_list_get_length (list); + popup_menu = e_attachment_view_get_popup_menu (view); - for (ii = 0; ii < length; ii++) { - button = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, ii)); + g_signal_connect ( + popup_menu, "deactivate", + G_CALLBACK (mail_display_attachment_menu_deactivate_cb), display); - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (button), "click", - G_CALLBACK (toggle_address_visibility), FALSE, - NULL); + mail_display_attachment_select_path (view, attachment); + display->priv->attachment_popup_position = *element_position; + + mail_display_attachment_inline_update_actions (display); + gtk_action_group_set_visible (display->priv->attachment_inline_group, TRUE); + + e_attachment_view_show_popup_menu (view, NULL, + mail_display_attachment_menu_position_cb, display); } - g_object_unref (list); + + g_clear_object (&attachment); } static void -setup_dom_bindings (WebKitWebView *web_view, - WebKitWebFrame *frame, - gpointer user_data) +mail_display_attachment_added_cb (EAttachmentStore *store, + EAttachment *attachment, + gpointer user_data) { - WebKitDOMDocument *document; + EMailDisplay *display = user_data; + guint flags; + + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - document = webkit_web_frame_get_dom_document (frame); + flags = e_attachment_get_initially_shown (attachment) ? E_ATTACHMENT_FLAG_VISIBLE : 0; - setup_image_click_event_listeners_on_document (document, web_view); + g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags)); } static void -mail_parts_bind_dom (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - WebKitWebFrame *frame; - WebKitLoadStatus load_status; - WebKitWebView *web_view; - WebKitDOMDocument *document; - EMailDisplay *display; - GQueue queue = G_QUEUE_INIT; - GList *head, *link; - const gchar *frame_name; +mail_display_attachment_removed_cb (EAttachmentStore *store, + EAttachment *attachment, + gpointer user_data) +{ + EMailDisplay *display = user_data; - frame = WEBKIT_WEB_FRAME (object); - load_status = webkit_web_frame_get_load_status (frame); + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - web_view = webkit_web_frame_get_web_view (frame); - display = E_MAIL_DISPLAY (web_view); + g_hash_table_remove (display->priv->attachment_flags, attachment); +} - if (load_status == WEBKIT_LOAD_PROVISIONAL) { - if (webkit_web_view_get_main_frame (web_view) == frame) - e_mail_display_cleanup_skipped_uris (display); - return; +static void +mail_element_exists_cb (GDBusProxy *web_extension, + GAsyncResult *result, + EMailPart *part) +{ + gboolean element_exists = FALSE; + GVariant *result_variant; + guint64 page_id; + + result_variant = g_dbus_proxy_call_finish (web_extension, result, NULL); + if (result_variant) { + g_variant_get (result_variant, "(bt)", &element_exists, &page_id); + g_variant_unref (result_variant); } - if (load_status != WEBKIT_LOAD_FINISHED) - return; + if (element_exists) + e_mail_part_bind_dom_element ( + part, + web_extension, + page_id, + e_mail_part_get_id (part)); + + g_object_unref (part); +} + +static void +mail_parts_bind_dom (EMailDisplay *display) +{ + EWebView *web_view; + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + GDBusProxy *web_extension; + gboolean has_attachment = FALSE; + + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); if (display->priv->part_list == NULL) return; initialize_web_view_colors (display); - frame_name = webkit_web_frame_get_name (frame); - if (frame_name == NULL || *frame_name == '\0') - frame_name = ".message.headers"; - document = webkit_web_view_get_dom_document (web_view); + web_view = E_WEB_VIEW (display); - e_mail_part_list_queue_parts ( - display->priv->part_list, frame_name, &queue); + web_extension = e_web_view_get_web_extension_proxy (web_view); + if (!web_extension) + return; + + e_mail_part_list_queue_parts (display->priv->part_list, NULL, &queue); head = g_queue_peek_head_link (&queue); for (link = head; link != NULL; link = g_list_next (link)) { EMailPart *part = E_MAIL_PART (link->data); - WebKitDOMElement *element; const gchar *part_id; - /* Iterate only the parts rendered in - * the frame and all it's subparts. */ - if (!e_mail_part_id_has_prefix (part, frame_name)) - break; - part_id = e_mail_part_get_id (part); - element = find_element_by_id (document, part_id); - if (element != NULL) - e_mail_part_bind_dom_element (part, element); + has_attachment = has_attachment || E_IS_MAIL_PART_ATTACHMENT (part); + + e_mail_part_web_view_loaded (part, web_view); + + g_dbus_proxy_call ( + web_extension, + "ElementExists", + g_variant_new ( + "(ts)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (display)), + part_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback)mail_element_exists_cb, + g_object_ref (part)); } while (!g_queue_is_empty (&queue)) g_object_unref (g_queue_pop_head (&queue)); + + if (has_attachment) { + e_web_view_register_element_clicked (web_view, "attachment-expander", + mail_display_attachment_expander_clicked_cb, NULL); + e_web_view_register_element_clicked (web_view, "attachment-menu", + mail_display_attachment_menu_clicked_cb, NULL); + } } static void -mail_display_frame_created (WebKitWebView *web_view, - WebKitWebFrame *frame, - gpointer user_data) +mail_display_load_changed_cb (WebKitWebView *wk_web_view, + WebKitLoadEvent load_event, + gpointer user_data) { - d (printf ("Frame %s created!\n", webkit_web_frame_get_name (frame))); + EMailDisplay *display; - /* Call bind_func of all parts written in this frame */ - g_signal_connect ( - frame, "notify::load-status", - G_CALLBACK (mail_parts_bind_dom), NULL); -} + g_return_if_fail (E_IS_MAIL_DISPLAY (wk_web_view)); -static void -mail_display_uri_changed (EMailDisplay *display, - GParamSpec *pspec, - gpointer dummy) -{ - d (printf ("EMailDisplay URI changed, recreating widgets hashtable\n")); + display = E_MAIL_DISPLAY (wk_web_view); - if (display->priv->widgets != NULL) { - g_hash_table_foreach ( - display->priv->widgets, - mail_display_plugin_widget_disconnect, display); - g_hash_table_destroy (display->priv->widgets); + if (load_event == WEBKIT_LOAD_STARTED) { + e_mail_display_cleanup_skipped_uris (display); + e_attachment_store_remove_all (display->priv->attachment_store); + return; } - display->priv->widgets = g_hash_table_new_full ( - (GHashFunc) g_str_hash, - (GEqualFunc) g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) e_weak_ref_free); + if (load_event == WEBKIT_LOAD_FINISHED) { + setup_dom_bindings (display); + mail_parts_bind_dom (display); + } } static void @@ -1387,6 +1249,20 @@ mail_display_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_ATTACHMENT_STORE: + g_value_set_object ( + value, + e_mail_display_get_attachment_store ( + E_MAIL_DISPLAY (object))); + return; + + case PROP_ATTACHMENT_VIEW: + g_value_set_object ( + value, + e_mail_display_get_attachment_view ( + E_MAIL_DISPLAY (object))); + return; + case PROP_FORMATTER: g_value_set_object ( value, @@ -1445,22 +1321,36 @@ mail_display_dispose (GObject *object) priv->scheduled_reload = 0; } - if (priv->widgets != NULL) { - g_hash_table_foreach ( - priv->widgets, - mail_display_plugin_widget_disconnect, object); - g_hash_table_destroy (priv->widgets); - priv->widgets = NULL; - } - if (priv->settings != NULL) g_signal_handlers_disconnect_matched ( priv->settings, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object); + if (priv->web_extension_headers_collapsed_signal_id > 0) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection ( + e_web_view_get_web_extension_proxy (E_WEB_VIEW (object))), + priv->web_extension_headers_collapsed_signal_id); + priv->web_extension_headers_collapsed_signal_id = 0; + } + + if (priv->attachment_store) { + /* To have called the mail_display_attachment_removed_cb() before it's disconnected */ + e_attachment_store_remove_all (priv->attachment_store); + + g_signal_handlers_disconnect_by_func (priv->attachment_store, + G_CALLBACK (mail_display_attachment_added_cb), object); + + g_signal_handlers_disconnect_by_func (priv->attachment_store, + G_CALLBACK (mail_display_attachment_removed_cb), object); + } + g_clear_object (&priv->part_list); g_clear_object (&priv->formatter); g_clear_object (&priv->settings); + g_clear_object (&priv->attachment_store); + g_clear_object (&priv->attachment_view); + g_clear_object (&priv->attachment_inline_group); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_mail_display_parent_class)->dispose (object); @@ -1484,6 +1374,7 @@ mail_display_finalize (GObject *object) priv->skipped_remote_content_sites = NULL; } + g_hash_table_destroy (priv->attachment_flags); g_clear_object (&priv->remote_content); g_mutex_unlock (&priv->remote_content_lock); g_mutex_clear (&priv->remote_content_lock); @@ -1493,12 +1384,107 @@ mail_display_finalize (GObject *object) } static void +mail_display_get_font_settings (GSettings *settings, + PangoFontDescription **monospace, + PangoFontDescription **variable) +{ + gboolean use_custom_font; + gchar *monospace_font; + gchar *variable_font; + + use_custom_font = g_settings_get_boolean (settings, "use-custom-font"); + + if (!use_custom_font) { + if (monospace) + *monospace = NULL; + if (variable) + *variable = NULL; + return; + } + + monospace_font = g_settings_get_string (settings, "monospace-font"); + variable_font = g_settings_get_string (settings, "variable-width-font"); + + if (monospace) + *monospace = (monospace_font != NULL) ? pango_font_description_from_string (monospace_font) : NULL; + if (variable) + *variable = (variable_font != NULL) ? pango_font_description_from_string (variable_font) : NULL; + + g_free (monospace_font); + g_free (variable_font); +} + +static void +mail_display_set_fonts (EWebView *web_view, + PangoFontDescription **monospace, + PangoFontDescription **variable) +{ + EMailDisplay *display = E_MAIL_DISPLAY (web_view); + + mail_display_get_font_settings (display->priv->settings, monospace, variable); +} + +static void +mail_display_web_view_initialize (WebKitWebView *web_view) +{ + WebKitSettings *webkit_settings; + + webkit_settings = webkit_web_view_get_settings (web_view); + + g_object_set (webkit_settings, + "enable-frame-flattening", TRUE, + NULL); +} + +static void mail_display_constructed (GObject *object) { + EContentRequest *content_request; + EWebView *web_view; + EMailDisplay *display; + GtkUIManager *ui_manager; + e_extensible_load_extensions (E_EXTENSIBLE (object)); /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_mail_display_parent_class)->constructed (object); + + mail_display_web_view_initialize (WEBKIT_WEB_VIEW (object)); + + display = E_MAIL_DISPLAY (object); + web_view = E_WEB_VIEW (object); + + e_web_view_update_fonts (web_view); + + content_request = e_http_request_new (); + e_web_view_register_content_request_for_scheme (web_view, "evo-http", content_request); + e_web_view_register_content_request_for_scheme (web_view, "evo-https", content_request); + g_object_unref (content_request); + + content_request = e_mail_request_new (); + e_web_view_register_content_request_for_scheme (web_view, "mail", content_request); + g_object_unref (content_request); + + content_request = e_cid_request_new (); + e_web_view_register_content_request_for_scheme (web_view, "cid", content_request); + g_object_unref (content_request); + + display->priv->attachment_view = g_object_ref_sink (e_attachment_bar_new (display->priv->attachment_store)); + + ui_manager = e_attachment_view_get_ui_manager (display->priv->attachment_view); + if (ui_manager) { + GError *error = NULL; + + gtk_ui_manager_insert_action_group (ui_manager, display->priv->attachment_inline_group, -1); + + display->priv->attachment_inline_ui_id = gtk_ui_manager_add_ui_from_string (ui_manager, + attachment_popup_ui, -1, &error); + + if (error) { + g_warning ("%s: Failed to read attachment_popup_ui: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } + } } static void @@ -1526,98 +1512,80 @@ static gboolean mail_display_button_press_event (GtkWidget *widget, GdkEventButton *event) { - EWebView *web_view = E_WEB_VIEW (widget); - WebKitHitTestResult *hit_test; - GList *list, *link; + if (event->button == 3) { + EWebView *web_view = E_WEB_VIEW (widget); + gchar *popup_document_uri; + GList *list, *link; - if (event->button != 3) - goto chainup; + popup_document_uri = e_web_view_get_document_uri_from_point (web_view, event->x, event->y); - hit_test = webkit_web_view_get_hit_test_result ( - WEBKIT_WEB_VIEW (web_view), event); + list = e_extensible_list_extensions ( + E_EXTENSIBLE (web_view), E_TYPE_EXTENSION); + for (link = list; link != NULL; link = g_list_next (link)) { + EExtension *extension = link->data; - list = e_extensible_list_extensions ( - E_EXTENSIBLE (web_view), E_TYPE_EXTENSION); - for (link = list; link != NULL; link = g_list_next (link)) { - EExtension *extension = link->data; + if (!E_IS_MAIL_DISPLAY_POPUP_EXTENSION (extension)) + continue; - if (!E_IS_MAIL_DISPLAY_POPUP_EXTENSION (extension)) - continue; + e_mail_display_popup_extension_update_actions ( + E_MAIL_DISPLAY_POPUP_EXTENSION (extension), popup_document_uri); + } - e_mail_display_popup_extension_update_actions ( - E_MAIL_DISPLAY_POPUP_EXTENSION (extension), hit_test); + g_list_free (list); + g_free (popup_document_uri); } - g_list_free (list); - - g_object_unref (hit_test); -chainup: /* Chain up to parent's button_press_event() method. */ return GTK_WIDGET_CLASS (e_mail_display_parent_class)-> button_press_event (widget, event); } -static gchar * -mail_display_redirect_uri (EWebView *web_view, - const gchar *uri) -{ - EMailDisplay *display; - EMailPartList *part_list; - gboolean uri_is_http; - display = E_MAIL_DISPLAY (web_view); - part_list = e_mail_display_get_part_list (display); +static gboolean +mail_display_image_exists_in_cache (const gchar *image_uri) +{ + gchar *filename; + gchar *hash; + gboolean exists = FALSE; - if (part_list == NULL) - goto chainup; + if (!emd_global_http_cache) + return FALSE; - /* Redirect cid:part_id to mail://mail_id/cid:part_id */ - if (g_str_has_prefix (uri, "cid:")) { - CamelFolder *folder; - const gchar *message_uid; + hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1); + filename = camel_data_cache_get_filename ( + emd_global_http_cache, "http", hash); - folder = e_mail_part_list_get_folder (part_list); - message_uid = e_mail_part_list_get_message_uid (part_list); + if (filename != NULL) { + struct stat st; - /* Always write raw content of CID object. */ - return e_mail_part_build_uri ( - folder, message_uid, - "part_id", G_TYPE_STRING, uri, - "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_CID, NULL); + exists = g_file_test (filename, G_FILE_TEST_EXISTS); + if (exists && g_stat (filename, &st) == 0) { + exists = st.st_size != 0; + } else { + exists = FALSE; + } + g_free (filename); } - /* WebKit won't allow to load a local file when displaying - * "remote" mail:// protocol, so we need to handle this manually. */ - if (g_str_has_prefix (uri, "file:")) { - gchar *content = NULL; - gchar *content_type; - gchar *filename; - gchar *encoded; - gchar *new_uri; - gsize length = 0; - - filename = g_filename_from_uri (uri, NULL, NULL); - if (filename == NULL) - goto chainup; - - if (!g_file_get_contents (filename, &content, &length, NULL)) { - g_free (filename); - goto chainup; - } + g_free (hash); - encoded = g_base64_encode ((guchar *) content, length); - content_type = g_content_type_guess (filename, NULL, 0, NULL); + return exists; +} - new_uri = g_strdup_printf ( - "data:%s;base64,%s", content_type, encoded); +static void +mail_display_uri_requested_cb (EWebView *web_view, + const gchar *uri, + gchar **redirect_to_uri) +{ + EMailDisplay *display; + EMailPartList *part_list; + gboolean uri_is_http; - g_free (content_type); - g_free (content); - g_free (filename); - g_free (encoded); + display = E_MAIL_DISPLAY (web_view); + part_list = e_mail_display_get_part_list (display); - return new_uri; - } + if (part_list == NULL) + return; uri_is_http = g_str_has_prefix (uri, "http:") || @@ -1639,7 +1607,8 @@ mail_display_redirect_uri (EWebView *web_view, can_download_uri = e_mail_display_can_download_uri (display, uri); if (!can_download_uri) { /* Check Evolution's cache */ - can_download_uri = mail_display_image_exists_in_cache (uri); + can_download_uri = mail_display_image_exists_in_cache ( + uri + (g_str_has_prefix (uri, "evo-") ? 4 : 0)); } /* If the URI is not cached and we are not allowed to load it @@ -1650,17 +1619,26 @@ mail_display_redirect_uri (EWebView *web_view, if (!can_download_uri && !display->priv->force_image_load && (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) { e_mail_display_claim_skipped_uri (display, uri); - return g_strdup ("about:blank"); + g_free (*redirect_to_uri); + *redirect_to_uri = g_strdup (""); + return; } folder = e_mail_part_list_get_folder (part_list); message_uid = e_mail_part_list_get_message_uid (part_list); - new_uri = g_strconcat ("evo-", uri, NULL); + if (g_str_has_prefix (uri, "evo-")) { + soup_uri = soup_uri_new (uri); + } else { + new_uri = g_strconcat ("evo-", uri, NULL); + soup_uri = soup_uri_new (new_uri); + + g_free (new_uri); + } + mail_uri = e_mail_part_build_uri ( folder, message_uid, NULL, NULL); - soup_uri = soup_uri_new (new_uri); if (soup_uri->query) query = soup_form_decode (soup_uri->query); else @@ -1682,22 +1660,18 @@ mail_display_redirect_uri (EWebView *web_view, g_free (mail_uri); soup_uri_set_query_from_form (soup_uri, query); - g_free (new_uri); new_uri = soup_uri_to_string (soup_uri, FALSE); soup_uri_free (soup_uri); g_hash_table_unref (query); - return new_uri; + g_free (*redirect_to_uri); + *redirect_to_uri = new_uri; } - -chainup: - /* Chain up to parent's redirect_uri() method. */ - return E_WEB_VIEW_CLASS (e_mail_display_parent_class)-> - redirect_uri (web_view, uri); } +#if 0 /* FIXME WK2 */ static CamelMimePart * camel_mime_part_from_cid (EMailDisplay *display, const gchar *uri) @@ -1745,60 +1719,6 @@ mail_display_suggest_filename (EWebView *web_view, } static void -mail_display_set_fonts (EWebView *web_view, - PangoFontDescription **monospace, - PangoFontDescription **variable) -{ - EMailDisplay *display = E_MAIL_DISPLAY (web_view); - gboolean use_custom_font; - gchar *monospace_font; - gchar *variable_font; - - use_custom_font = g_settings_get_boolean ( - display->priv->settings, "use-custom-font"); - if (!use_custom_font) { - *monospace = NULL; - *variable = NULL; - return; - } - - monospace_font = g_settings_get_string ( - display->priv->settings, "monospace-font"); - variable_font = g_settings_get_string ( - display->priv->settings, "variable-width-font"); - - *monospace = (monospace_font != NULL) ? - pango_font_description_from_string (monospace_font) : NULL; - *variable = (variable_font != NULL) ? - pango_font_description_from_string (variable_font) : NULL; - - g_free (monospace_font); - g_free (variable_font); -} - -static void -e_mail_display_test_change_and_update_fonts_cb (EMailDisplay *mail_display, - const gchar *key, - GSettings *settings) -{ - GVariant *new_value, *old_value; - - new_value = g_settings_get_value (settings, key); - old_value = g_hash_table_lookup (mail_display->priv->old_settings, key); - - if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) { - if (new_value) - g_hash_table_insert (mail_display->priv->old_settings, g_strdup (key), new_value); - else - g_hash_table_remove (mail_display->priv->old_settings, key); - - e_web_view_update_fonts (E_WEB_VIEW (mail_display)); - } else if (new_value) { - g_variant_unref (new_value); - } -} - -static void mail_display_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *data, @@ -1859,6 +1779,29 @@ mail_display_drag_data_get (GtkWidget *widget, out: g_free (uri); } +#endif + +static void +e_mail_display_test_change_and_update_fonts_cb (EMailDisplay *mail_display, + const gchar *key, + GSettings *settings) +{ + GVariant *new_value, *old_value; + + new_value = g_settings_get_value (settings, key); + old_value = g_hash_table_lookup (mail_display->priv->old_settings, key); + + if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) { + if (new_value) + g_hash_table_insert (mail_display->priv->old_settings, g_strdup (key), new_value); + else + g_hash_table_remove (mail_display->priv->old_settings, key); + + e_web_view_update_fonts (E_WEB_VIEW (mail_display)); + } else if (new_value) { + g_variant_unref (new_value); + } +} static void e_mail_display_class_init (EMailDisplayClass *class) @@ -1882,12 +1825,35 @@ e_mail_display_class_init (EMailDisplayClass *class) widget_class->button_press_event = mail_display_button_press_event; web_view_class = E_WEB_VIEW_CLASS (class); - web_view_class->redirect_uri = mail_display_redirect_uri; +#if 0 /* FIXME WK2 */ web_view_class->suggest_filename = mail_display_suggest_filename; +#endif web_view_class->set_fonts = mail_display_set_fonts; g_object_class_install_property ( object_class, + PROP_ATTACHMENT_STORE, + g_param_spec_object ( + "attachment-store", + "Attachment Store", + NULL, + E_TYPE_ATTACHMENT_STORE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_ATTACHMENT_VIEW, + g_param_spec_object ( + "attachment-view", + "Attachment View", + NULL, + E_TYPE_ATTACHMENT_VIEW, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, PROP_FORMATTER, g_param_spec_pointer ( "formatter", @@ -1956,13 +1922,24 @@ static void e_mail_display_init (EMailDisplay *display) { GtkUIManager *ui_manager; - const gchar *user_cache_dir; - WebKitWebSettings *settings; - WebKitWebFrame *main_frame; GtkActionGroup *actions; display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display); + display->priv->attachment_store = E_ATTACHMENT_STORE (e_attachment_store_new ()); + display->priv->attachment_flags = g_hash_table_new (g_direct_hash, g_direct_equal); + display->priv->attachment_inline_group = gtk_action_group_new ("e-mail-display-attachment-inline"); + + gtk_action_group_add_actions ( + display->priv->attachment_inline_group, attachment_inline_entries, + G_N_ELEMENTS (attachment_inline_entries), display); + gtk_action_group_set_visible (display->priv->attachment_inline_group, FALSE); + + g_signal_connect (display->priv->attachment_store, "attachment-added", + G_CALLBACK (mail_display_attachment_added_cb), display); + g_signal_connect (display->priv->attachment_store, "attachment-removed", + G_CALLBACK (mail_display_attachment_removed_cb), display); + display->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); /* Set invalid mode so that MODE property initialization is run @@ -1972,39 +1949,18 @@ e_mail_display_init (EMailDisplay *display) display->priv->force_image_load = FALSE; display->priv->scheduled_reload = 0; - webkit_web_view_set_full_content_zoom (WEBKIT_WEB_VIEW (display), TRUE); - - settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (display)); - g_object_set (settings, "enable-frame-flattening", TRUE, NULL); - g_signal_connect ( - display, "navigation-policy-decision-requested", - G_CALLBACK (mail_display_link_clicked), NULL); - g_signal_connect ( - display, "resource-request-starting", - G_CALLBACK (mail_display_resource_requested), NULL); + display, "decide-policy", + G_CALLBACK (decide_policy_cb), NULL); + g_signal_connect ( display, "process-mailto", G_CALLBACK (mail_display_process_mailto), NULL); - g_signal_connect ( - display, "create-plugin-widget", - G_CALLBACK (mail_display_plugin_widget_requested), NULL); - g_signal_connect ( - display, "frame-created", - G_CALLBACK (mail_display_frame_created), NULL); - e_signal_connect_notify ( - display, "notify::uri", - G_CALLBACK (mail_display_uri_changed), NULL); - g_signal_connect ( - display, "document-load-finished", - G_CALLBACK (setup_dom_bindings), NULL); - g_signal_connect ( - display, "document-load-finished", - G_CALLBACK (initialize_web_view_colors), NULL); +#if 0 /* FIXME WK2 */ g_signal_connect_after ( display, "drag-data-get", G_CALLBACK (mail_display_drag_data_get), display); - +#endif display->priv->settings = e_util_ref_settings ("org.gnome.evolution.mail"); g_signal_connect_swapped ( display->priv->settings , "changed::monospace-font", @@ -2016,12 +1972,9 @@ e_mail_display_init (EMailDisplay *display) display->priv->settings , "changed::use-custom-font", G_CALLBACK (e_mail_display_test_change_and_update_fonts_cb), display); - e_web_view_update_fonts (E_WEB_VIEW (display)); - - main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display)); - e_signal_connect_notify ( - main_frame, "notify::load-status", - G_CALLBACK (mail_parts_bind_dom), NULL); + g_signal_connect ( + display, "load-changed", + G_CALLBACK (mail_display_load_changed_cb), NULL); actions = e_web_view_get_action_group (E_WEB_VIEW (display), "mailto"); gtk_action_group_add_actions ( @@ -2030,16 +1983,14 @@ e_mail_display_init (EMailDisplay *display) ui_manager = e_web_view_get_ui_manager (E_WEB_VIEW (display)); gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL); - e_web_view_install_request_handler ( - E_WEB_VIEW (display), E_TYPE_MAIL_REQUEST); - e_web_view_install_request_handler ( - E_WEB_VIEW (display), E_TYPE_HTTP_REQUEST); - e_web_view_install_request_handler ( - E_WEB_VIEW (display), E_TYPE_FILE_REQUEST); - e_web_view_install_request_handler ( - E_WEB_VIEW (display), E_TYPE_STOCK_REQUEST); + g_mutex_init (&display->priv->remote_content_lock); + display->priv->remote_content = NULL; + display->priv->skipped_remote_content_sites = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL); + + g_signal_connect (display, "uri-requested", G_CALLBACK (mail_display_uri_requested_cb), NULL); if (emd_global_http_cache == NULL) { + const gchar *user_cache_dir; GError *error = NULL; user_cache_dir = e_get_user_cache_dir (); @@ -2058,10 +2009,6 @@ e_mail_display_init (EMailDisplay *display) g_clear_error (&error); } } - - g_mutex_init (&display->priv->remote_content_lock); - display->priv->remote_content = NULL; - display->priv->skipped_remote_content_sites = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL); } static void @@ -2088,6 +2035,46 @@ e_mail_display_update_colors (EMailDisplay *display, g_free (color_value); } +static void +e_mail_display_claim_attachment (EMailFormatter *formatter, + EAttachment *attachment, + gpointer user_data) +{ + EMailDisplay *display = user_data; + GList *attachments; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_return_if_fail (E_IS_MAIL_DISPLAY (display)); + + attachments = e_attachment_store_get_attachments (display->priv->attachment_store); + + if (!g_list_find (attachments, attachment)) { + e_attachment_store_add_attachment (display->priv->attachment_store, attachment); + + if (e_attachment_is_mail_note (attachment)) { + CamelFolder *folder; + const gchar *message_uid; + + folder = e_mail_part_list_get_folder (display->priv->part_list); + message_uid = e_mail_part_list_get_message_uid (display->priv->part_list); + + if (folder && message_uid) { + CamelMessageInfo *info; + + info = camel_folder_get_message_info (folder, message_uid); + if (info) { + if (!camel_message_info_get_user_flag (info, E_MAIL_NOTES_USER_FLAG)) + camel_message_info_set_user_flag (info, E_MAIL_NOTES_USER_FLAG, TRUE); + camel_message_info_unref (info); + } + } + } + } + + g_list_free_full (attachments, g_object_unref); +} + GtkWidget * e_mail_display_new (EMailRemoteContent *remote_content) { @@ -2096,6 +2083,22 @@ e_mail_display_new (EMailRemoteContent *remote_content) NULL); } +EAttachmentStore * +e_mail_display_get_attachment_store (EMailDisplay *display) +{ + g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL); + + return display->priv->attachment_store; +} + +EAttachmentView * +e_mail_display_get_attachment_view (EMailDisplay *display) +{ + g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL); + + return display->priv->attachment_view; +} + EMailFormatterMode e_mail_display_get_mode (EMailDisplay *display) { @@ -2178,6 +2181,8 @@ e_mail_display_set_mode (EMailDisplay *display, G_CALLBACK (e_mail_display_reload), display, NULL); + g_signal_connect (formatter, "claim-attachment", G_CALLBACK (e_mail_display_claim_attachment), display); + e_mail_display_reload (display); g_object_notify (G_OBJECT (display), "mode"); @@ -2281,7 +2286,7 @@ e_mail_display_load (EMailDisplay *display, g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - display->priv->force_image_load = FALSE; + e_mail_display_set_force_load_images (display, FALSE); part_list = display->priv->part_list; if (part_list == NULL) { @@ -2442,85 +2447,42 @@ e_mail_display_set_status (EMailDisplay *display, g_free (str); } -static gchar * -mail_display_get_frame_selection_text (WebKitDOMElement *iframe) -{ - WebKitDOMDocument *document; - WebKitDOMDOMWindow *window; - WebKitDOMDOMSelection *selection; - WebKitDOMNodeList *frames; - gulong ii, length; - - document = webkit_dom_html_iframe_element_get_content_document ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)); - window = webkit_dom_document_get_default_view (document); - selection = webkit_dom_dom_window_get_selection (window); - if (selection && (webkit_dom_dom_selection_get_range_count (selection) > 0)) { - WebKitDOMRange *range; - - range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL); - if (range != NULL) - return webkit_dom_range_to_string (range, NULL); - } - - frames = webkit_dom_document_get_elements_by_tag_name ( - document, "IFRAME"); - length = webkit_dom_node_list_get_length (frames); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *node; - gchar *text; - - node = webkit_dom_node_list_item (frames, ii); - - text = mail_display_get_frame_selection_text ( - WEBKIT_DOM_ELEMENT (node)); - - g_object_unref (node); - if (text != NULL) { - g_object_unref (frames); - return text; - } - } - - g_object_unref (frames); - - return NULL; -} - -gchar * -e_mail_display_get_selection_plain_text (EMailDisplay *display) +const gchar * +e_mail_display_get_selection_plain_text_sync (EMailDisplay *display, + GCancellable *cancellable, + GError **error) { - WebKitDOMDocument *document; - WebKitDOMNodeList *frames; - gulong ii, length; + GDBusProxy *web_extension; g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL); - +/* FIXME WK2 if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (display))) return NULL; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display)); - frames = webkit_dom_document_get_elements_by_tag_name (document, "IFRAME"); - length = webkit_dom_node_list_get_length (frames); - - for (ii = 0; ii < length; ii++) { - gchar *text; - WebKitDOMNode *node; - - node = webkit_dom_node_list_item (frames, ii); - - text = mail_display_get_frame_selection_text ( - WEBKIT_DOM_ELEMENT (node)); - - g_object_unref (node); - if (text != NULL) { - g_object_unref (frames); - return text; +*/ + web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (display)); + if (web_extension) { + GVariant *result; + const gchar *text_content = NULL; + + result = g_dbus_proxy_call_sync ( + web_extension, + "GetDocumentContentText", + g_variant_new ( + "(t)", + webkit_web_view_get_page_id ( + WEBKIT_WEB_VIEW (display))), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + + if (result) { + g_variant_get (result, "(&s)", &text_content); + g_variant_unref (result); + return text_content; } } - g_object_unref (frames); - return NULL; } @@ -2529,7 +2491,7 @@ e_mail_display_load_images (EMailDisplay *display) { g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - display->priv->force_image_load = TRUE; + e_mail_display_set_force_load_images (display, TRUE); e_web_view_reload (E_WEB_VIEW (display)); } @@ -2539,6 +2501,9 @@ e_mail_display_set_force_load_images (EMailDisplay *display, { g_return_if_fail (E_IS_MAIL_DISPLAY (display)); + if ((display->priv->force_image_load ? 1 : 0) == (force_load_images ? 1 : 0)) + return; + display->priv->force_image_load = force_load_images; } @@ -2617,37 +2582,3 @@ e_mail_display_set_remote_content (EMailDisplay *display, g_mutex_unlock (&display->priv->remote_content_lock); } - -gboolean -e_mail_display_needs_key (EMailDisplay *mail_display, - gboolean with_input) -{ - gboolean needs_key = FALSE; - - g_return_val_if_fail (E_IS_MAIL_DISPLAY (mail_display), FALSE); - - if (gtk_widget_has_focus (GTK_WIDGET (mail_display))) { - WebKitWebFrame *frame; - WebKitDOMDocument *dom; - WebKitDOMElement *element; - gchar *name = NULL; - - frame = webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (mail_display)); - if (!frame) - return FALSE; - dom = webkit_web_frame_get_dom_document (frame); - element = webkit_dom_html_document_get_active_element (WEBKIT_DOM_HTML_DOCUMENT (dom)); - - if (element) - name = webkit_dom_node_get_node_name (WEBKIT_DOM_NODE (element)); - - /* if INPUT or TEXTAREA has focus, then any key press should go there */ - if (name && ((with_input && g_ascii_strcasecmp (name, "INPUT") == 0) || g_ascii_strcasecmp (name, "TEXTAREA") == 0)) { - needs_key = TRUE; - } - - g_free (name); - } - - return needs_key; -} diff --git a/mail/e-mail-display.h b/mail/e-mail-display.h index 893f172..3670f91 100644 --- a/mail/e-mail-display.h +++ b/mail/e-mail-display.h @@ -62,6 +62,12 @@ struct _EMailDisplayClass { GType e_mail_display_get_type (void) G_GNUC_CONST; GtkWidget * e_mail_display_new (EMailRemoteContent *remote_content); +EAttachmentStore * + e_mail_display_get_attachment_store + (EMailDisplay *display); +EAttachmentView * + e_mail_display_get_attachment_view + (EMailDisplay *display); EMailFormatterMode e_mail_display_get_mode (EMailDisplay *display); void e_mail_display_set_mode (EMailDisplay *display, @@ -88,8 +94,10 @@ GtkAction * e_mail_display_get_action (EMailDisplay *display, const gchar *action_name); void e_mail_display_set_status (EMailDisplay *display, const gchar *status); -gchar * e_mail_display_get_selection_plain_text - (EMailDisplay *display); +const gchar * e_mail_display_get_selection_plain_text_sync + (EMailDisplay *display, + GCancellable *cancellable, + GError **error); void e_mail_display_load_images (EMailDisplay *display); void e_mail_display_set_force_load_images (EMailDisplay *display, @@ -104,8 +112,6 @@ EMailRemoteContent * void e_mail_display_set_remote_content (EMailDisplay *display, EMailRemoteContent *remote_content); -gboolean e_mail_display_needs_key (EMailDisplay *mail_display, - gboolean with_input); G_END_DECLS diff --git a/mail/e-mail-notes.c b/mail/e-mail-notes.c index 7d69cb9..94a8396 100644 --- a/mail/e-mail-notes.c +++ b/mail/e-mail-notes.c @@ -94,12 +94,12 @@ e_mail_notes_extract_text_content (CamelMimePart *part) } static void -e_mail_notes_extract_text_from_multipart_alternative (EHTMLEditorView *view, +e_mail_notes_extract_text_from_multipart_alternative (EContentEditor *cnt_editor, CamelMultipart *in_multipart) { guint ii, nparts; - g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view)); + g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor)); g_return_if_fail (CAMEL_IS_MULTIPART (in_multipart)); nparts = camel_multipart_get_number (in_multipart); @@ -122,8 +122,12 @@ e_mail_notes_extract_text_from_multipart_alternative (EHTMLEditorView *view, text = e_mail_notes_extract_text_content (part); if (text) { - e_html_editor_view_set_html_mode (view, TRUE); - e_html_editor_view_set_text_html (view, text); + e_content_editor_set_html_mode (cnt_editor, TRUE); + e_content_editor_insert_content ( + cnt_editor, + text, + E_CONTENT_EDITOR_INSERT_TEXT_HTML | + E_CONTENT_EDITOR_INSERT_REPLACE_ALL); g_free (text); break; } @@ -132,7 +136,11 @@ e_mail_notes_extract_text_from_multipart_alternative (EHTMLEditorView *view, text = e_mail_notes_extract_text_content (part); if (text) { - e_html_editor_view_set_text_plain (view, text); + e_content_editor_insert_content ( + cnt_editor, + text, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN | + E_CONTENT_EDITOR_INSERT_REPLACE_ALL); g_free (text); } break; @@ -144,13 +152,13 @@ static void e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes_editor, CamelMultipart *multipart) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; guint ii, nparts; g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor)); g_return_if_fail (CAMEL_IS_MULTIPART (multipart)); - view = e_html_editor_get_view (notes_editor->editor); + cnt_editor = e_html_editor_get_content_editor (notes_editor->editor); nparts = camel_multipart_get_number (multipart); for (ii = 0; ii < nparts; ii++) { @@ -167,12 +175,11 @@ e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes continue; if (camel_content_type_is (ct, "image", "*")) { - e_html_editor_view_add_inline_image_from_mime_part (view, part); + e_content_editor_insert_image_from_mime_part (cnt_editor, part); } else if (camel_content_type_is (ct, "multipart", "alternative")) { content = camel_medium_get_content (CAMEL_MEDIUM (part)); - if (CAMEL_IS_MULTIPART (content)) { - e_mail_notes_extract_text_from_multipart_alternative (view, CAMEL_MULTIPART (content)); - } + if (CAMEL_IS_MULTIPART (content)) + e_mail_notes_extract_text_from_multipart_alternative (cnt_editor, CAMEL_MULTIPART (content)); } } } @@ -183,7 +190,7 @@ e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor, { CamelContentType *ct; CamelDataWrapper *content; - EHTMLEditorView *view; + EContentEditor *cnt_editor; g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor)); g_return_if_fail (CAMEL_IS_MIME_PART (part)); @@ -194,7 +201,7 @@ e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor, g_return_if_fail (content != NULL); g_return_if_fail (ct != NULL); - view = e_html_editor_get_view (notes_editor->editor); + cnt_editor = e_html_editor_get_content_editor (notes_editor->editor); if (camel_content_type_is (ct, "multipart", "related")) { g_return_if_fail (CAMEL_IS_MULTIPART (content)); @@ -202,14 +209,18 @@ e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor, e_mail_notes_editor_extract_text_from_multipart_related (notes_editor, CAMEL_MULTIPART (content)); } else if (camel_content_type_is (ct, "multipart", "alternative")) { if (CAMEL_IS_MULTIPART (content)) { - e_mail_notes_extract_text_from_multipart_alternative (view, CAMEL_MULTIPART (content)); + e_mail_notes_extract_text_from_multipart_alternative (cnt_editor, CAMEL_MULTIPART (content)); } } else if (camel_content_type_is (ct, "text", "plain")) { gchar *text; text = e_mail_notes_extract_text_content (part); if (text) { - e_html_editor_view_set_text_plain (view, text); + e_content_editor_insert_content ( + cnt_editor, + text, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN | + E_CONTENT_EDITOR_INSERT_REPLACE_ALL); g_free (text); } } @@ -221,7 +232,7 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor, { CamelContentType *ct; CamelDataWrapper *content; - EHTMLEditorView *view; + EContentEditor *cnt_editor; g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor)); g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); @@ -232,7 +243,7 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor, g_return_if_fail (content != NULL); g_return_if_fail (ct != NULL); - view = e_html_editor_get_view (notes_editor->editor); + cnt_editor = e_html_editor_get_content_editor (notes_editor->editor); if (camel_content_type_is (ct, "multipart", "mixed")) { EAttachmentStore *attachment_store; @@ -276,13 +287,13 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor, e_mail_notes_editor_extract_text_from_part (notes_editor, CAMEL_MIME_PART (message)); } - e_html_editor_view_set_changed (view, FALSE); + e_content_editor_set_changed (cnt_editor, FALSE); } static CamelMimeMessage * e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; EAttachmentStore *attachment_store; CamelMimeMessage *message = NULL; gchar *message_uid; @@ -293,8 +304,8 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor) g_return_val_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor), NULL); g_return_val_if_fail (notes_editor->editor, NULL); - view = e_html_editor_get_view (notes_editor->editor); - g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL); + cnt_editor = e_html_editor_get_content_editor (notes_editor->editor); + g_return_val_if_fail (E_IS_CONTENT_EDITOR (cnt_editor), NULL); message = camel_mime_message_new (); username = g_get_user_name (); @@ -316,18 +327,23 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor) attachment_store = e_attachment_view_get_store (E_ATTACHMENT_VIEW (notes_editor->attachment_paned)); has_attachments = e_attachment_store_get_num_attachments (attachment_store) > 0; - if (e_html_editor_view_get_html_mode (view)) { + if (e_content_editor_get_html_mode (cnt_editor)) { CamelMultipart *multipart_alternative; CamelMultipart *multipart_body; CamelMimePart *part; - GList *inline_images = NULL; + GSList *inline_images_parts = NULL; gchar *text; multipart_alternative = camel_multipart_new (); camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_alternative), "multipart/alternative"); camel_multipart_set_boundary (multipart_alternative, NULL); - text = e_html_editor_view_get_text_plain (view); + text = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_PLAIN | + E_CONTENT_EDITOR_GET_PROCESSED, + NULL, NULL); + if (text && *text) { part = camel_mime_part_new (); camel_mime_part_set_content (part, text, strlen (text), "text/plain"); @@ -340,7 +356,14 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor) g_free (text); - text = e_html_editor_view_get_text_html (view, g_get_host_name (), &inline_images); + text = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_PROCESSED | + E_CONTENT_EDITOR_GET_TEXT_HTML | + E_CONTENT_EDITOR_GET_INLINE_IMAGES, + g_get_host_name (), + &inline_images_parts); + if (has_attachments && !has_text && (!text || !*text)) { /* Text is required, thus if there are attachments, but no text, then store at least a space. */ @@ -357,14 +380,14 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor) has_text = TRUE; } else { - g_list_free_full (inline_images, g_object_unref); - inline_images = NULL; + g_slist_free_full (inline_images_parts, g_object_unref); + inline_images_parts = NULL; } g_free (text); - if (inline_images) { - GList *link; + if (inline_images_parts) { + GSList *link; multipart_body = camel_multipart_new (); camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_body), "multipart/related"); @@ -375,7 +398,7 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor) camel_multipart_add_part (multipart_body, part); g_object_unref (part); - for (link = inline_images; link; link = g_list_next (link)) { + for (link = inline_images_parts; link; link = g_slist_next (link)) { CamelMimePart *part = link->data; if (!part) @@ -408,13 +431,17 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor) camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (multipart_body)); - g_list_free_full (inline_images, g_object_unref); + g_slist_free_full (inline_images_parts, g_object_unref); g_clear_object (&multipart_alternative); g_clear_object (&multipart_body); } else { gchar *text; - text = e_html_editor_view_get_text_plain (view); + text = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_PLAIN | + E_CONTENT_EDITOR_GET_PROCESSED, + NULL, NULL); if (has_attachments && !has_text && (!text || !*text)) { /* Text is required, thus if there are attachments, @@ -560,17 +587,17 @@ notes_editor_activity_notify_cb (EActivityBar *activity_bar, GParamSpec *param, EMailNotesEditor *notes_editor) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; GtkAction *action; gboolean can_edit; g_return_if_fail (E_IS_ACTIVITY_BAR (activity_bar)); g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor)); - view = e_html_editor_get_view (notes_editor->editor); + cnt_editor = e_html_editor_get_content_editor (notes_editor->editor); can_edit = notes_editor->had_message && !e_activity_bar_get_activity (activity_bar); - webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), can_edit); + g_object_set (cnt_editor, "editable", can_edit, NULL); action = gtk_action_group_get_action (notes_editor->action_group, "save-and-close"); gtk_action_set_sensitive (action, can_edit); @@ -711,12 +738,12 @@ static void action_close_cb (GtkAction *action, EMailNotesEditor *notes_editor) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; gboolean something_changed = FALSE; - view = e_html_editor_get_view (notes_editor->editor); + cnt_editor = e_html_editor_get_content_editor (notes_editor->editor); - something_changed = webkit_web_view_can_undo (WEBKIT_WEB_VIEW (view)); + something_changed = e_content_editor_get_changed (cnt_editor); if (something_changed) { gint response; @@ -874,9 +901,10 @@ e_mail_notes_editor_init (EMailNotesEditor *notes_editor) } static EMailNotesEditor * -e_mail_notes_editor_new (GtkWindow *parent, - CamelFolder *folder, - const gchar *uid) +e_mail_notes_editor_new_with_editor (EHTMLEditor *html_editor, + GtkWindow *parent, + CamelFolder *folder, + const gchar *uid) { const gchar *ui = "<ui>\n" @@ -921,7 +949,7 @@ e_mail_notes_editor_new (GtkWindow *parent, }; EMailNotesEditor *notes_editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; EFocusTracker *focus_tracker; EActivityBar *activity_bar; GtkUIManager *ui_manager; @@ -948,10 +976,10 @@ e_mail_notes_editor_new (GtkWindow *parent, content = widget; - widget = e_html_editor_new (); + widget = GTK_WIDGET (html_editor); - notes_editor->editor = E_HTML_EDITOR (widget); - view = e_html_editor_get_view (notes_editor->editor); + notes_editor->editor = html_editor; + cnt_editor = e_html_editor_get_content_editor (notes_editor->editor); ui_manager = e_html_editor_get_ui_manager (notes_editor->editor); /* Because we are loading from a hard-coded string, there is @@ -987,6 +1015,12 @@ e_mail_notes_editor_new (GtkWindow *parent, gtk_widget_show (widget); widget = GTK_WIDGET (notes_editor->editor); + g_object_set (G_OBJECT (widget), + "halign", GTK_ALIGN_FILL, + "hexpand", TRUE, + "valign", GTK_ALIGN_FILL, + "vexpand", TRUE, + NULL); gtk_box_pack_start (GTK_BOX (content), widget, TRUE, TRUE, 0); gtk_widget_show (widget); @@ -996,7 +1030,7 @@ e_mail_notes_editor_new (GtkWindow *parent, gtk_widget_show (widget); e_binding_bind_property ( - view, "editable", + cnt_editor, "editable", widget, "sensitive", G_BINDING_SYNC_CREATE); @@ -1017,10 +1051,11 @@ e_mail_notes_editor_new (GtkWindow *parent, notes_editor->focus_tracker = focus_tracker; - gtk_widget_grab_focus (GTK_WIDGET (view)); + gtk_widget_grab_focus (GTK_WIDGET (cnt_editor)); settings = e_util_ref_settings ("org.gnome.evolution.mail"); - e_html_editor_view_set_html_mode (view, g_settings_get_boolean (settings, "composer-send-html")); + e_content_editor_set_html_mode ( + cnt_editor, g_settings_get_boolean (settings, "composer-send-html")); g_object_unref (settings); g_signal_connect ( @@ -1039,29 +1074,79 @@ e_mail_notes_editor_new (GtkWindow *parent, return notes_editor; } +typedef struct _AsyncData { + GtkWindow *parent; + CamelFolder *folder; + gchar *uid; +} AsyncData; + +static void +async_data_free (gpointer ptr) +{ + AsyncData *ad = ptr; + + if (ad) { + g_clear_object (&ad->parent); + g_clear_object (&ad->folder); + g_free (ad->uid); + g_free (ad); + } +} + +static void +e_mail_notes_editor_ready_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + AsyncData *ad = user_data; + GtkWidget *html_editor; + GError *error = NULL; + + g_return_if_fail (result != NULL); + g_return_if_fail (ad != NULL); + + html_editor = e_html_editor_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + EMailNotesEditor *notes_editor; + EActivityBar *activity_bar; + EActivity *activity; + + notes_editor = e_mail_notes_editor_new_with_editor (E_HTML_EDITOR (html_editor), + ad->parent, ad->folder, ad->uid); + + activity_bar = e_html_editor_get_activity_bar (notes_editor->editor); + activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor), + _("Retrieving message..."), "mail:no-retrieve-message", NULL, + e_mail_notes_retrieve_message_thread, + g_object_ref (notes_editor), e_mail_notes_retrieve_message_done); + e_activity_bar_set_activity (activity_bar, activity); + g_clear_object (&activity); + + gtk_widget_show (GTK_WIDGET (notes_editor)); + } + + async_data_free (ad); +} + void e_mail_notes_edit (GtkWindow *parent, CamelFolder *folder, const gchar *uid) { - EMailNotesEditor *notes_editor; - EActivityBar *activity_bar; - EActivity *activity; + AsyncData *ad; g_return_if_fail (CAMEL_IS_FOLDER (folder)); g_return_if_fail (uid != NULL); - notes_editor = e_mail_notes_editor_new (parent, folder, uid); - - activity_bar = e_html_editor_get_activity_bar (notes_editor->editor); - activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor), - _("Retrieving message..."), "mail:no-retrieve-message", NULL, - e_mail_notes_retrieve_message_thread, - g_object_ref (notes_editor), e_mail_notes_retrieve_message_done); - e_activity_bar_set_activity (activity_bar, activity); - g_clear_object (&activity); + ad = g_new0 (AsyncData, 1); + ad->parent = parent ? g_object_ref (parent) : NULL; + ad->folder = g_object_ref (folder); + ad->uid = g_strdup (uid); - gtk_widget_show (GTK_WIDGET (notes_editor)); + e_html_editor_new (e_mail_notes_editor_ready_cb, ad); } gboolean diff --git a/mail/e-mail-paned-view.c b/mail/e-mail-paned-view.c index 47bc059..cf5635e 100644 --- a/mail/e-mail-paned-view.c +++ b/mail/e-mail-paned-view.c @@ -675,7 +675,7 @@ mail_paned_view_constructed (GObject *object) EMailView *view; GtkWidget *message_list; GtkWidget *container; - GtkWidget *widget; + GtkWidget *widget, *vbox; priv = E_MAIL_PANED_VIEW_GET_PRIVATE (object); @@ -737,8 +737,13 @@ mail_paned_view_constructed (GObject *object) container = priv->paned; + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); widget = e_preview_pane_new (E_WEB_VIEW (priv->display)); - gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE); + + gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (e_mail_display_get_attachment_view (priv->display)), FALSE, FALSE, 0); + + gtk_paned_pack2 (GTK_PANED (container), vbox, FALSE, FALSE); priv->preview_pane = g_object_ref (widget); gtk_widget_show (GTK_WIDGET (priv->display)); gtk_widget_show (widget); @@ -748,6 +753,11 @@ mail_paned_view_constructed (GObject *object) widget, "visible", G_BINDING_SYNC_CREATE); + e_binding_bind_property ( + object, "preview-visible", + vbox, "visible", + G_BINDING_SYNC_CREATE); + /* Load the view instance. */ e_mail_view_update_view_instance (E_MAIL_VIEW (object)); diff --git a/mail/e-mail-printer.c b/mail/e-mail-printer.c index 2208ad6..15ab8b8 100644 --- a/mail/e-mail-printer.c +++ b/mail/e-mail-printer.c @@ -23,7 +23,7 @@ #include <glib/gi18n.h> #include <gtk/gtk.h> -#include <webkit/webkitdom.h> +#include <camel/camel.h> #include "e-util/e-util.h" @@ -49,9 +49,6 @@ struct _EMailPrinterPrivate { gchar *export_filename; - WebKitWebView *web_view; /* WebView to print from */ - - GtkPrintOperation *operation; GtkPrintOperationAction print_action; }; @@ -101,8 +98,9 @@ async_context_free (AsyncContext *async_context) g_slice_free (AsyncContext, async_context); } +#if 0 /* FIXME WK2 */ static GtkWidget * -mail_printer_create_custom_widget_cb (GtkPrintOperation *operation, +mail_printer_create_custom_widget_cb (WebKitPrintOperation *operation, AsyncContext *async_context) { EMailDisplay *display; @@ -110,7 +108,7 @@ mail_printer_create_custom_widget_cb (GtkPrintOperation *operation, EMailPart *part; GtkWidget *widget; - gtk_print_operation_set_custom_tab_label (operation, _("Headers")); + webkit_print_operation_set_custom_tab_label (operation, _("Headers")); display = E_MAIL_DISPLAY (async_context->web_view); part_list = e_mail_display_get_part_list (display); @@ -128,7 +126,7 @@ mail_printer_create_custom_widget_cb (GtkPrintOperation *operation, } static void -mail_printer_custom_widget_apply_cb (GtkPrintOperation *operation, +mail_printer_custom_widget_apply_cb (WebKitPrintOperation *operation, GtkWidget *widget, AsyncContext *async_context) { @@ -171,6 +169,33 @@ mail_printer_draw_footer_cb (GtkPrintOperation *operation, g_object_unref (layout); g_free (text); } +#endif +static void +mail_printer_print_finished_cb (WebKitPrintOperation *print_operation, + GSimpleAsyncResult *simple) +{ + if (camel_debug ("wex")) + printf ("%s\n", G_STRFUNC); +} + +static void +mail_printer_print_failed_cb (WebKitPrintOperation *print_operation, + GError *error, + GSimpleAsyncResult *simple) +{ + AsyncContext *async_context; + + if (camel_debug ("wex")) + printf ("%s\n", G_STRFUNC); + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); + else + g_warning ("WebKit print operation returned ERROR result without setting a GError"); + + async_context->print_result = GTK_PRINT_OPERATION_RESULT_ERROR; +} static gboolean mail_printer_print_timeout_cb (gpointer user_data) @@ -178,34 +203,32 @@ mail_printer_print_timeout_cb (gpointer user_data) GSimpleAsyncResult *simple; AsyncContext *async_context; GCancellable *cancellable; - GtkPrintOperation *print_operation; + WebKitPrintOperation *print_operation; +/* FIXME WK2 EMailPrinter *printer; GtkPrintOperationAction print_action; - EMailPrinter *printer; - WebKitWebFrame *web_frame; - gulong create_custom_widget_handler_id; - gulong custom_widget_apply_handler_id; gulong draw_page_handler_id; + gulong create_custom_widget_handler_id; + gulong custom_widget_apply_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); - +*/ + print_operation = webkit_print_operation_new (async_context->web_view); +/* if (async_context->print_action == GTK_PRINT_OPERATION_ACTION_EXPORT) { const gchar *export_filename; @@ -214,7 +237,8 @@ mail_printer_print_timeout_cb (gpointer user_data) 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 (mail_printer_create_custom_widget_cb), @@ -224,42 +248,29 @@ mail_printer_print_timeout_cb (gpointer user_data) print_operation, "custom-widget-apply", G_CALLBACK (mail_printer_custom_widget_apply_cb), async_context); +*/ + g_signal_connect ( + print_operation, "failed", + G_CALLBACK (mail_printer_print_failed_cb), + async_context); + + g_signal_connect ( + print_operation, "finished", + G_CALLBACK (mail_printer_print_finished_cb), + async_context); +/* FIXME WK2 - this will be hard to add back to WK2 API.. There is a CSS draft + * that can be used to add a page numbers, but it is not in WebKit yet. + * http://www.w3.org/TR/css3-page/ 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 setting 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"); - break; - 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 (); - } - +*/ + webkit_print_operation_run_dialog ( + print_operation, + GTK_WINDOW (gtk_widget_get_toplevel (gtk_widget_get_toplevel (GTK_WIDGET (async_context->web_view))))); +/* FIXME WK2 g_signal_handler_disconnect ( print_operation, create_custom_widget_handler_id); @@ -268,10 +279,11 @@ mail_printer_print_timeout_cb (gpointer user_data) g_signal_handler_disconnect ( print_operation, draw_page_handler_id); - +*/ g_object_unref (print_operation); - g_object_unref (printer); +/* + g_object_unref (printer);*/ exit: if (error != NULL) @@ -283,18 +295,16 @@ exit: } static void -mail_printer_load_status_cb (WebKitWebView *web_view, - GParamSpec *pspec, - GSimpleAsyncResult *simple) +mail_printer_load_changed_cb (WebKitWebView *web_view, + WebKitLoadEvent load_event, + 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) + if (load_event != WEBKIT_LOAD_FINISHED) return; /* Signal handlers are holding the only GSimpleAsyncResult @@ -341,7 +351,7 @@ mail_printer_new_web_view (const gchar *charset, const gchar *default_charset) { WebKitWebView *web_view; - WebKitWebSettings *web_settings; + WebKitSettings *web_settings; EMailFormatter *formatter; web_view = g_object_new ( @@ -446,8 +456,6 @@ mail_printer_dispose (GObject *object) g_clear_object (&priv->formatter); g_clear_object (&priv->part_list); g_clear_object (&priv->remote_content); - g_clear_object (&priv->web_view); - g_clear_object (&priv->operation); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_mail_printer_parent_class)->dispose (object); @@ -587,8 +595,8 @@ e_mail_printer_print (EMailPrinter *printer, 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), + web_view, "load-changed", + G_CALLBACK (mail_printer_load_changed_cb), g_object_ref (simple), (GClosureNotify) g_object_unref, 0); async_context->load_status_handler_id = handler_id; diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c index fed2e85..c850bba 100644 --- a/mail/e-mail-reader-utils.c +++ b/mail/e-mail-reader-utils.c @@ -1729,6 +1729,76 @@ e_mail_reader_remove_duplicates (EMailReader *reader) g_ptr_array_unref (uids); } +typedef struct _CreateComposerData { + EMailReader *reader; + CamelFolder *folder; + CamelMimeMessage *message; + gchar *message_uid; + gboolean keep_signature; + + EMailPartList *part_list; + EMailReplyType reply_type; + EMailReplyStyle reply_style; + CamelInternetAddress *address; + EMailPartValidityFlags validity_pgp_sum; + EMailPartValidityFlags validity_smime_sum; + + EMailForwardStyle forward_style; + + CamelMimePart *attached_part; + gchar *attached_subject; + GPtrArray *attached_uids; +} CreateComposerData; + +static void +create_composer_data_free (CreateComposerData *ccd) +{ + if (ccd) { + if (ccd->attached_uids) + g_ptr_array_unref (ccd->attached_uids); + + g_clear_object (&ccd->reader); + g_clear_object (&ccd->folder); + g_clear_object (&ccd->message); + g_clear_object (&ccd->part_list); + g_clear_object (&ccd->address); + g_clear_object (&ccd->attached_part); + g_free (ccd->message_uid); + g_free (ccd->attached_subject); + g_free (ccd); + } +} + +static void +mail_reader_edit_messages_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateComposerData *ccd = user_data; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (ccd != NULL); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + camel_medium_remove_header ( + CAMEL_MEDIUM (ccd->message), "X-Mailer"); + + em_utils_edit_message ( + composer, ccd->folder, ccd->message, ccd->message_uid, + ccd->keep_signature); + + e_mail_reader_composer_created ( + ccd->reader, composer, ccd->message); + } + + create_composer_data_free (ccd); +} + static void mail_reader_edit_messages_cb (GObject *source_object, GAsyncResult *result, @@ -1780,24 +1850,17 @@ mail_reader_edit_messages_cb (GObject *source_object, g_hash_table_iter_init (&iter, hash_table); while (g_hash_table_iter_next (&iter, &key, &value)) { - EMsgComposer *composer; - CamelMimeMessage *message; - const gchar *message_uid = NULL; - - if (async_context->replace) - message_uid = (const gchar *) key; - - message = CAMEL_MIME_MESSAGE (value); + CreateComposerData *ccd; - camel_medium_remove_header ( - CAMEL_MEDIUM (message), "X-Mailer"); + ccd = g_new0 (CreateComposerData, 1); + ccd->reader = g_object_ref (async_context->reader); + ccd->folder = g_object_ref (folder); + ccd->message = g_object_ref (CAMEL_MIME_MESSAGE (value)); - composer = em_utils_edit_message ( - shell, folder, message, message_uid, - async_context->keep_signature); + if (async_context->replace) + ccd->message_uid = g_strdup ((const gchar *) key); - e_mail_reader_composer_created ( - async_context->reader, composer, message); + e_msg_composer_new (shell, mail_reader_edit_messages_composer_created_cb, ccd); } g_hash_table_unref (hash_table); @@ -1843,6 +1906,44 @@ e_mail_reader_edit_messages (EMailReader *reader, } static void +mail_reader_forward_attached_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateComposerData *ccd = user_data; + EMsgComposer *composer; + GError *error = NULL; + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + CamelDataWrapper *content; + + em_utils_forward_attachment (composer, ccd->attached_part, ccd->attached_subject, ccd->folder, ccd->attached_uids); + + content = camel_medium_get_content (CAMEL_MEDIUM (ccd->attached_part)); + if (CAMEL_IS_MIME_MESSAGE (content)) { + e_mail_reader_composer_created (ccd->reader, composer, CAMEL_MIME_MESSAGE (content)); + } else { + /* XXX What to do for the multipart/digest case? + * Extract the first message from the digest, or + * change the argument type to CamelMimePart and + * just pass the whole digest through? + * + * This signal is primarily serving EMailBrowser, + * which can only forward one message at a time. + * So for the moment it doesn't matter, but still + * something to consider. */ + e_mail_reader_composer_created (ccd->reader, composer, NULL); + } + } + + create_composer_data_free (ccd); +} + +static void mail_reader_forward_attachment_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) @@ -1851,9 +1952,9 @@ mail_reader_forward_attachment_cb (GObject *source_object, EMailBackend *backend; EActivity *activity; EAlertSink *alert_sink; + EShell *shell; CamelMimePart *part; - CamelDataWrapper *content; - EMsgComposer *composer; + CreateComposerData *ccd; gchar *subject = NULL; AsyncContext *async_context; GError *local_error = NULL; @@ -1887,40 +1988,53 @@ mail_reader_forward_attachment_cb (GObject *source_object, goto exit; } + ccd = g_new0 (CreateComposerData, 1); + ccd->reader = g_object_ref (async_context->reader); + ccd->folder = g_object_ref (folder); + ccd->attached_part = part; + ccd->attached_subject = subject; + ccd->attached_uids = async_context->uids ? g_ptr_array_ref (async_context->uids) : NULL; + backend = e_mail_reader_get_backend (async_context->reader); + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); - composer = em_utils_forward_attachment ( - backend, part, subject, folder, async_context->uids); - - content = camel_medium_get_content (CAMEL_MEDIUM (part)); - if (CAMEL_IS_MIME_MESSAGE (content)) { - e_mail_reader_composer_created ( - async_context->reader, composer, - CAMEL_MIME_MESSAGE (content)); - } else { - /* XXX What to do for the multipart/digest case? - * Extract the first message from the digest, or - * change the argument type to CamelMimePart and - * just pass the whole digest through? - * - * This signal is primarily serving EMailBrowser, - * which can only forward one message at a time. - * So for the moment it doesn't matter, but still - * something to consider. */ - e_mail_reader_composer_created ( - async_context->reader, composer, NULL); - } + e_msg_composer_new (shell, mail_reader_forward_attached_composer_created_cb, ccd); e_activity_set_state (activity, E_ACTIVITY_COMPLETED); - g_object_unref (part); - g_free (subject); - exit: async_context_free (async_context); } static void +mail_reader_forward_message_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateComposerData *ccd = user_data; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (ccd != NULL); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + em_utils_forward_message ( + composer, ccd->message, + ccd->forward_style, + ccd->folder, ccd->message_uid); + + e_mail_reader_composer_created ( + ccd->reader, composer, ccd->message); + } + + create_composer_data_free (ccd); +} + +static void mail_reader_forward_messages_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) @@ -1929,6 +2043,7 @@ mail_reader_forward_messages_cb (GObject *source_object, EMailBackend *backend; EActivity *activity; EAlertSink *alert_sink; + EShell *shell; GHashTable *hash_table; GHashTableIter iter; gpointer key, value; @@ -1942,6 +2057,7 @@ mail_reader_forward_messages_cb (GObject *source_object, alert_sink = e_activity_get_alert_sink (activity); backend = e_mail_reader_get_backend (async_context->reader); + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); hash_table = e_mail_folder_get_multiple_messages_finish ( folder, result, &local_error); @@ -1969,20 +2085,21 @@ mail_reader_forward_messages_cb (GObject *source_object, g_hash_table_iter_init (&iter, hash_table); while (g_hash_table_iter_next (&iter, &key, &value)) { - EMsgComposer *composer; + CreateComposerData *ccd; CamelMimeMessage *message; const gchar *message_uid; message_uid = (const gchar *) key; message = CAMEL_MIME_MESSAGE (value); - composer = em_utils_forward_message ( - backend, message, - async_context->forward_style, - folder, message_uid); + ccd = g_new0 (CreateComposerData, 1); + ccd->reader = g_object_ref (async_context->reader); + ccd->folder = g_object_ref (folder); + ccd->message = g_object_ref (message); + ccd->message_uid = g_strdup (message_uid); + ccd->forward_style = async_context->forward_style; - e_mail_reader_composer_created ( - async_context->reader, composer, message); + e_msg_composer_new (shell, mail_reader_forward_message_composer_created_cb, ccd); } g_hash_table_unref (hash_table); @@ -2087,6 +2204,41 @@ html_contains_nonwhitespace (const gchar *html, } static void +mail_reader_reply_composer_created_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + AsyncContext *async_context = user_data; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (async_context != NULL); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + CamelMimeMessage *message; + + message = e_mail_part_list_get_message (async_context->part_list); + + em_utils_reply_to_message ( + composer, message, + async_context->folder, + async_context->message_uid, + async_context->reply_type, + async_context->reply_style, + async_context->part_list, + async_context->address); + + e_mail_reader_composer_created (async_context->reader, composer, message); + } + + async_context_free (async_context); +} + +static void mail_reader_reply_message_parsed (GObject *object, GAsyncResult *result, gpointer user_data) @@ -2094,15 +2246,12 @@ mail_reader_reply_message_parsed (GObject *object, EShell *shell; EMailBackend *backend; EMailReader *reader = E_MAIL_READER (object); - EMailPartList *part_list; - EMsgComposer *composer; - CamelMimeMessage *message; AsyncContext *async_context; GError *local_error = NULL; async_context = (AsyncContext *) user_data; - part_list = e_mail_reader_parse_message_finish (reader, result, &local_error); + async_context->part_list = e_mail_reader_parse_message_finish (reader, result, &local_error); if (local_error) { g_warn_if_fail (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)); @@ -2113,25 +2262,10 @@ mail_reader_reply_message_parsed (GObject *object, return; } - message = e_mail_part_list_get_message (part_list); - backend = e_mail_reader_get_backend (async_context->reader); shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); - composer = em_utils_reply_to_message ( - shell, message, - async_context->folder, - async_context->message_uid, - async_context->reply_type, - async_context->reply_style, - part_list, - async_context->address); - - e_mail_reader_composer_created (reader, composer, message); - - g_object_unref (part_list); - - async_context_free (async_context); + e_msg_composer_new (shell, mail_reader_reply_composer_created_cb, async_context); } static void @@ -2186,6 +2320,60 @@ mail_reader_get_message_ready_cb (GObject *source_object, g_object_unref (message); } +static void +mail_reader_reply_to_message_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateComposerData *ccd = user_data; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (ccd != NULL); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + em_utils_reply_to_message ( + composer, ccd->message, ccd->folder, ccd->message_uid, + ccd->reply_type, ccd->reply_style, ccd->part_list, ccd->address); + + if (ccd->validity_pgp_sum != 0 || ccd->validity_smime_sum != 0) { + GtkToggleAction *action; + + if ((ccd->validity_pgp_sum & E_MAIL_PART_VALIDITY_PGP) != 0) { + if ((ccd->validity_pgp_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) { + action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer)); + gtk_toggle_action_set_active (action, TRUE); + } + + if ((ccd->validity_pgp_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) { + action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer)); + gtk_toggle_action_set_active (action, TRUE); + } + } + + if ((ccd->validity_smime_sum & E_MAIL_PART_VALIDITY_SMIME) != 0) { + if ((ccd->validity_smime_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) { + action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer)); + gtk_toggle_action_set_active (action, TRUE); + } + + if ((ccd->validity_smime_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) { + action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer)); + gtk_toggle_action_set_active (action, TRUE); + } + } + } + + e_mail_reader_composer_created (ccd->reader, composer, ccd->message); + } + + create_composer_data_free (ccd); +} + void e_mail_reader_reply_to_message (EMailReader *reader, CamelMimeMessage *src_message, @@ -2208,7 +2396,7 @@ e_mail_reader_reply_to_message (EMailReader *reader, gint length; gchar *mail_uri; CamelObjectBag *registry; - EMsgComposer *composer; + CreateComposerData *ccd; EMailPartValidityFlags validity_pgp_sum = 0; EMailPartValidityFlags validity_smime_sum = 0; @@ -2305,7 +2493,7 @@ e_mail_reader_reply_to_message (EMailReader *reader, if (!e_web_view_is_selection_active (web_view)) goto whole_message; - selection = e_web_view_get_selection_html (web_view); + selection = e_web_view_get_selection_content_html_sync (web_view, NULL, NULL); if (selection == NULL || *selection == '\0') goto whole_message; @@ -2338,42 +2526,16 @@ e_mail_reader_reply_to_message (EMailReader *reader, CAMEL_MIME_PART (new_message), selection, length, "text/html; charset=utf-8"); - composer = em_utils_reply_to_message ( - shell, new_message, folder, uid, - reply_type, reply_style, NULL, address); - if (validity_pgp_sum != 0 || validity_smime_sum != 0) { - GtkToggleAction *action; - - if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_PGP) != 0) { - if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) { - action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer)); - gtk_toggle_action_set_active (action, TRUE); - } - - if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) { - action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer)); - gtk_toggle_action_set_active (action, TRUE); - } - } - - if ((validity_smime_sum & E_MAIL_PART_VALIDITY_SMIME) != 0) { - if ((validity_smime_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) { - action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer)); - gtk_toggle_action_set_active (action, TRUE); - } - - if ((validity_smime_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) { - action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer)); - gtk_toggle_action_set_active (action, TRUE); - } - } - } - - e_mail_reader_composer_created (reader, composer, new_message); + ccd = g_new0 (CreateComposerData, 1); + ccd->reader = g_object_ref (reader); + ccd->folder = g_object_ref (folder); + ccd->message_uid = g_strdup (uid); + ccd->message = new_message; + ccd->reply_type = reply_type; + ccd->reply_style = reply_style; + ccd->address = address ? g_object_ref (address) : NULL; - g_object_unref (new_message); - - g_free (selection); + e_msg_composer_new (shell, mail_reader_reply_to_message_composer_created_cb, ccd); goto exit; @@ -2408,14 +2570,21 @@ whole_message: g_object_unref (activity); } else { - composer = em_utils_reply_to_message ( - shell, src_message, folder, uid, - reply_type, reply_style, part_list, address); - - e_mail_reader_composer_created (reader, composer, src_message); + ccd = g_new0 (CreateComposerData, 1); + ccd->reader = g_object_ref (reader); + ccd->folder = g_object_ref (folder); + ccd->message_uid = g_strdup (uid); + ccd->message = g_object_ref (src_message); + ccd->reply_type = reply_type; + ccd->reply_style = reply_style; + ccd->part_list = part_list ? g_object_ref (part_list) : NULL; + ccd->address = address ? g_object_ref (address) : NULL; + + e_msg_composer_new (shell, mail_reader_reply_to_message_composer_created_cb, ccd); } exit: + g_free (selection); g_clear_object (&address); g_clear_object (&folder); } diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c index b215f79..8fe32f4 100644 --- a/mail/e-mail-reader.c +++ b/mail/e-mail-reader.c @@ -915,6 +915,43 @@ action_mail_message_edit_cb (GtkAction *action, g_ptr_array_unref (uids); } +typedef struct _CreateComposerData { + EMailReader *reader; + CamelMimeMessage *message; + CamelFolder *folder; + gboolean is_redirect; +} CreateComposerData; + +static void +mail_reader_new_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateComposerData *ccd = user_data; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (ccd != NULL); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + if (ccd->is_redirect) + em_utils_redirect_message (composer, ccd->message); + else + em_utils_compose_new_message (composer, ccd->folder); + + e_mail_reader_composer_created (ccd->reader, composer, ccd->message); + } + + g_clear_object (&ccd->reader); + g_clear_object (&ccd->message); + g_clear_object (&ccd->folder); + g_free (ccd); +} + static void action_mail_message_new_cb (GtkAction *action, EMailReader *reader) @@ -923,7 +960,7 @@ action_mail_message_new_cb (GtkAction *action, EMailBackend *backend; EShellBackend *shell_backend; CamelFolder *folder; - EMsgComposer *composer; + CreateComposerData *ccd; folder = e_mail_reader_ref_folder (reader); backend = e_mail_reader_get_backend (reader); @@ -931,11 +968,12 @@ action_mail_message_new_cb (GtkAction *action, shell_backend = E_SHELL_BACKEND (backend); shell = e_shell_backend_get_shell (shell_backend); - composer = em_utils_compose_new_message (shell, folder); - - e_mail_reader_composer_created (reader, composer, NULL); + ccd = g_new0 (CreateComposerData, 1); + ccd->reader = g_object_ref (reader); + ccd->folder = folder; + ccd->is_redirect = FALSE; - g_clear_object (&folder); + e_msg_composer_new (shell, mail_reader_new_composer_created_cb, ccd); } static void @@ -1140,7 +1178,7 @@ mail_reader_redirect_cb (CamelFolder *folder, EMailBackend *backend; EAlertSink *alert_sink; CamelMimeMessage *message; - EMsgComposer *composer; + CreateComposerData *ccd; GError *error = NULL; alert_sink = e_activity_get_alert_sink (closure->activity); @@ -1168,11 +1206,12 @@ mail_reader_redirect_cb (CamelFolder *folder, backend = e_mail_reader_get_backend (closure->reader); shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); - composer = em_utils_redirect_message (shell, message); + ccd = g_new0 (CreateComposerData, 1); + ccd->reader = g_object_ref (closure->reader); + ccd->message = message; + ccd->is_redirect = TRUE; - e_mail_reader_composer_created (closure->reader, composer, message); - - g_object_unref (message); + e_msg_composer_new (shell, mail_reader_new_composer_created_cb, ccd); mail_reader_closure_free (closure); } @@ -2640,33 +2679,22 @@ mail_reader_key_press_event_cb (EMailReader *reader, const gchar *action_name; if (!gtk_widget_has_focus (GTK_WIDGET (reader))) { - WebKitWebFrame *frame; - WebKitDOMDocument *dom; - WebKitDOMElement *element; EMailDisplay *display; - gchar *name = NULL; + GDBusProxy *web_extension; display = e_mail_reader_get_mail_display (reader); - frame = webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (display)); - - if (frame != NULL) { - dom = webkit_web_frame_get_dom_document (frame); - element = webkit_dom_html_document_get_active_element (WEBKIT_DOM_HTML_DOCUMENT (dom)); + web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (display)); + if (web_extension) { + GVariant *result; - if (element != NULL) { - name = webkit_dom_node_get_node_name (WEBKIT_DOM_NODE (element)); - g_object_unref (element); - } + result = g_dbus_proxy_get_cached_property (web_extension, "NeedInput"); + if (result) { + gboolean need_input = g_variant_get_boolean (result); + g_variant_unref (result); - /* If INPUT or TEXTAREA has focus, - * then any key press should go there. */ - if (name != NULL && - (g_ascii_strcasecmp (name, "INPUT") == 0 || - g_ascii_strcasecmp (name, "TEXTAREA") == 0)) { - g_free (name); - return FALSE; + if (need_input) + return FALSE; } - g_free (name); } } @@ -2849,13 +2877,13 @@ schedule_timeout_mark_seen (EMailReader *reader) } static void -mail_reader_load_status_changed_cb (EMailReader *reader, - GParamSpec *pspec, - EMailDisplay *display) +mail_reader_load_changed_cb (EMailReader *reader, + WebKitLoadEvent event, + EMailDisplay *display) { EMailReaderPrivate *priv; - if (webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (display)) != WEBKIT_LOAD_FINISHED) + if (event != WEBKIT_LOAD_FINISHED) return; priv = E_MAIL_READER_GET_PRIVATE (reader); @@ -4405,9 +4433,9 @@ connect_signals: display, "key-press-event", G_CALLBACK (mail_reader_key_press_event_cb), reader); - e_signal_connect_notify_swapped ( - display, "notify::load-status", - G_CALLBACK (mail_reader_load_status_changed_cb), reader); + g_signal_connect_swapped ( + display, "load-changed", + G_CALLBACK (mail_reader_load_changed_cb), reader); g_signal_connect_swapped ( message_list, "message-selected", @@ -5500,37 +5528,27 @@ e_mail_reader_create_remote_content_alert_button (EMailReader *reader) } static void -e_mail_reader_load_status_notify_cb (WebKitWebFrame *frame, - GParamSpec *param, +mail_reader_display_load_changed_cb (EMailDisplay *mail_display, + WebKitLoadEvent load_event, EMailReader *reader) { - WebKitLoadStatus load_status; - EMailDisplay *mail_display; EMailReaderPrivate *priv; - g_return_if_fail (WEBKIT_IS_WEB_FRAME (frame)); + g_return_if_fail (E_IS_MAIL_DISPLAY (mail_display)); g_return_if_fail (E_IS_MAIL_READER (reader)); priv = E_MAIL_READER_GET_PRIVATE (reader); g_return_if_fail (priv != NULL); - load_status = webkit_web_frame_get_load_status (frame); - if (load_status == WEBKIT_LOAD_PROVISIONAL) { - WebKitWebView *web_view; - - web_view = webkit_web_frame_get_web_view (frame); - - if (priv->remote_content_alert && webkit_web_view_get_main_frame (web_view) == frame) + if (load_event == WEBKIT_LOAD_STARTED) { + if (priv->remote_content_alert) e_alert_response (priv->remote_content_alert, GTK_RESPONSE_CLOSE); return; } - if (load_status != WEBKIT_LOAD_FINISHED) + if (load_event != WEBKIT_LOAD_FINISHED) return; - mail_display = e_mail_reader_get_mail_display (reader); - g_return_if_fail (E_IS_MAIL_DISPLAY (mail_display)); - if (!e_mail_display_has_skipped_remote_content_sites (mail_display)) return; @@ -5552,18 +5570,6 @@ e_mail_reader_load_status_notify_cb (WebKitWebFrame *frame, } } -static void -mail_reader_display_frame_created_cb (WebKitWebView *web_view, - WebKitWebFrame *frame, - EMailReader *reader) -{ - g_return_if_fail (E_IS_MAIL_DISPLAY (web_view)); - g_return_if_fail (E_IS_MAIL_READER (reader)); - - e_signal_connect_notify (frame, "notify::load-status", - G_CALLBACK (e_mail_reader_load_status_notify_cb), reader); -} - /** * e_mail_reader_connect_remote_content: * @reader: an #EMailReader @@ -5575,18 +5581,12 @@ void e_mail_reader_connect_remote_content (EMailReader *reader) { EMailDisplay *mail_display; - WebKitWebFrame *frame; g_return_if_fail (E_IS_MAIL_READER (reader)); mail_display = e_mail_reader_get_mail_display (reader); g_return_if_fail (E_IS_MAIL_DISPLAY (mail_display)); - frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (mail_display)); - - e_signal_connect_notify (frame, "notify::load-status", - G_CALLBACK (e_mail_reader_load_status_notify_cb), reader); - - g_signal_connect (mail_display, "frame-created", - G_CALLBACK (mail_reader_display_frame_created_cb), reader); + g_signal_connect (mail_display, "load-changed", + G_CALLBACK (mail_reader_display_load_changed_cb), reader); } diff --git a/mail/e-mail-request.c b/mail/e-mail-request.c index ce673e9..a5ef505 100644 --- a/mail/e-mail-request.c +++ b/mail/e-mail-request.c @@ -15,19 +15,15 @@ * */ -#define LIBSOUP_USE_UNSTABLE_REQUEST_API - -#include "e-mail-request.h" -#include "em-utils.h" +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif #include <libsoup/soup.h> -#include <libsoup/soup-requester.h> -#include <libsoup/soup-request-http.h> - -#include <webkit/webkit.h> #include <glib/gi18n.h> #include <camel/camel.h> +#include <libedataserver/libedataserver.h> #include "shell/e-shell.h" @@ -35,85 +31,127 @@ #include "em-format/e-mail-formatter-utils.h" #include "em-format/e-mail-formatter-print.h" +#include "em-utils.h" +#include "e-mail-display.h" #include "e-mail-ui-session.h" +#include "e-mail-request.h" #define d(x) -#define dd(x) - -#define E_MAIL_REQUEST_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_MAIL_REQUEST, EMailRequestPrivate)) struct _EMailRequestPrivate { - GBytes *bytes; - gchar *mime_type; + gint dummy; +}; - GHashTable *uri_query; - gchar *uri_base; - gchar *full_uri; +static void e_mail_request_content_request_init (EContentRequestInterface *iface); - gboolean part_converted_to_utf8; - gchar *ret_mime_type; -}; +G_DEFINE_TYPE_WITH_CODE (EMailRequest, e_mail_request, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_mail_request_content_request_init)) -static const gchar *data_schemes[] = { "mail", NULL }; +static gboolean +e_mail_request_can_process_uri (EContentRequest *request, + const gchar *uri) +{ + g_return_val_if_fail (E_IS_MAIL_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); -G_DEFINE_TYPE (EMailRequest, e_mail_request, SOUP_TYPE_REQUEST) + return g_ascii_strncasecmp (uri, "mail:", 5) == 0; +} static void -handle_mail_request (GSimpleAsyncResult *simple, - GObject *object, - GCancellable *cancellable) +save_gicon_to_stream (GIcon *icon, + gint size, + GOutputStream *output_stream, + gchar **out_mime_type) +{ + GtkIconInfo *icon_info; + GdkPixbuf *pixbuf; + + if (size < 16) + size = 16; + + icon_info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (), icon, size, GTK_ICON_LOOKUP_FORCE_SIZE); + if (!icon_info) + return; + + pixbuf = gtk_icon_info_load_icon (icon_info, NULL); + if (pixbuf) { + if (gdk_pixbuf_save_to_stream ( + pixbuf, output_stream, + "png", NULL, NULL, NULL)) { + *out_mime_type = g_strdup ("image/png"); + } + g_object_unref (pixbuf); + } + + g_object_unref (icon); +} + +static gboolean +mail_request_process_mail_sync (EContentRequest *request, + SoupURI *suri, + GHashTable *uri_query, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error) { - EMailRequest *request = E_MAIL_REQUEST (object); EMailFormatter *formatter; EMailPartList *part_list; CamelObjectBag *registry; - GInputStream *input_stream; GOutputStream *output_stream; + GBytes *bytes; + gchar *tmp, *use_mime_type = NULL; const gchar *val; const gchar *default_charset, *charset; + gboolean part_converted_to_utf8 = FALSE; EMailFormatterContext context = { 0 }; - if (g_cancellable_is_cancelled (cancellable)) - return; + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + tmp = g_strdup_printf ("%s://%s%s", suri->scheme, suri->host, suri->path); registry = e_mail_part_list_get_registry (); - part_list = camel_object_bag_get (registry, request->priv->uri_base); + part_list = camel_object_bag_get (registry, tmp); + + g_free (tmp); + + context.uri = soup_uri_to_string (suri, FALSE); if (camel_debug_start ("emformat:requests")) { - printf ("%s: found part-list %p for full_uri '%s'\n", G_STRFUNC, part_list, request->priv->full_uri); + printf ("%s: found part-list %p for full_uri '%s'\n", G_STRFUNC, part_list, context.uri); camel_debug_end (); } - if (!part_list) - return; + if (!part_list) { + g_free (context.uri); + return FALSE; + } - val = g_hash_table_lookup ( - request->priv->uri_query, "headers_collapsed"); + val = g_hash_table_lookup (uri_query, "headers_collapsed"); if (val != NULL && atoi (val) == 1) context.flags |= E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED; - val = g_hash_table_lookup ( - request->priv->uri_query, "headers_collapsable"); + val = g_hash_table_lookup (uri_query, "headers_collapsable"); if (val != NULL && atoi (val) == 1) context.flags |= E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSABLE; - val = g_hash_table_lookup (request->priv->uri_query, "mode"); + val = g_hash_table_lookup (uri_query, "mode"); if (val != NULL) context.mode = atoi (val); - default_charset = g_hash_table_lookup ( - request->priv->uri_query, "formatter_default_charset"); - charset = g_hash_table_lookup ( - request->priv->uri_query, "formatter_charset"); + default_charset = g_hash_table_lookup (uri_query, "formatter_default_charset"); + charset = g_hash_table_lookup (uri_query, "formatter_charset"); context.part_list = g_object_ref (part_list); - context.uri = request->priv->full_uri; if (context.mode == E_MAIL_FORMATTER_MODE_PRINTING) formatter = e_mail_formatter_print_new (); + else if (E_IS_MAIL_DISPLAY (requester)) + formatter = g_object_ref (e_mail_display_get_formatter (E_MAIL_DISPLAY (requester))); else formatter = e_mail_formatter_new (); @@ -124,7 +162,58 @@ handle_mail_request (GSimpleAsyncResult *simple, output_stream = g_memory_output_stream_new_resizable (); - val = g_hash_table_lookup (request->priv->uri_query, "part_id"); + val = g_hash_table_lookup (uri_query, "attachment_icon"); + if (val) { + gchar *attachment_id; + + attachment_id = soup_uri_decode (val); + if (E_IS_MAIL_DISPLAY (requester)) { + EMailDisplay *mail_display = E_MAIL_DISPLAY (requester); + EAttachmentStore *attachment_store; + GList *attachments, *link; + + attachment_store = e_mail_display_get_attachment_store (mail_display); + attachments = e_attachment_store_get_attachments (attachment_store); + for (link = attachments; link; link = g_list_next (link)) { + EAttachment *attachment = link->data; + gboolean can_use; + + tmp = g_strdup_printf ("%p", attachment); + can_use = g_strcmp0 (tmp, attachment_id) == 0; + g_free (tmp); + + if (can_use) { + GtkTreeIter iter; + + if (e_attachment_store_find_attachment_iter (attachment_store, attachment, &iter)) { + GIcon *icon = NULL; + + gtk_tree_model_get (GTK_TREE_MODEL (attachment_store), &iter, + E_ATTACHMENT_STORE_COLUMN_ICON, &icon, + -1); + + if (icon) { + const gchar *size = g_hash_table_lookup (uri_query, "size"); + if (!size) + size = "16"; + + save_gicon_to_stream (icon, atoi (size), output_stream, &use_mime_type); + } + } + + break; + } + } + + g_list_free_full (attachments, g_object_unref); + } + + g_free (attachment_id); + + goto no_part; + } + + val = g_hash_table_lookup (uri_query, "part_id"); if (val != NULL) { EMailPart *part; const gchar *mime_type; @@ -143,40 +232,20 @@ handle_mail_request (GSimpleAsyncResult *simple, } g_free (part_id); - mime_type = g_hash_table_lookup ( - request->priv->uri_query, "mime_type"); + mime_type = g_hash_table_lookup (uri_query, "mime_type"); if (context.mode == E_MAIL_FORMATTER_MODE_SOURCE) mime_type = "application/vnd.evolution.source"; - if (context.mode == E_MAIL_FORMATTER_MODE_CID) { - CamelDataWrapper *dw; - CamelMimePart *mime_part; - - mime_part = e_mail_part_ref_mime_part (part); - dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); - g_return_if_fail (dw); - - if (!mime_type) { - g_free (request->priv->mime_type); - request->priv->mime_type = camel_data_wrapper_get_mime_type (dw); - } - - camel_data_wrapper_decode_to_output_stream_sync ( - dw, output_stream, cancellable, NULL); - - g_object_unref (mime_part); - } else { - if (mime_type == NULL) - mime_type = e_mail_part_get_mime_type (part); + if (mime_type == NULL) + mime_type = e_mail_part_get_mime_type (part); - e_mail_formatter_format_as ( - formatter, &context, part, - output_stream, mime_type, - cancellable); + e_mail_formatter_format_as ( + formatter, &context, part, + output_stream, mime_type, + cancellable); - request->priv->part_converted_to_utf8 = e_mail_part_get_converted_to_utf8 (part); - } + part_converted_to_utf8 = e_mail_part_get_converted_to_utf8 (part); g_object_unref (part); @@ -191,61 +260,54 @@ handle_mail_request (GSimpleAsyncResult *simple, g_output_stream_close (output_stream, NULL, NULL); - if (request->priv->bytes != NULL) - g_bytes_unref (request->priv->bytes); - - request->priv->bytes = g_memory_output_stream_steal_as_bytes ( - G_MEMORY_OUTPUT_STREAM (output_stream)); + bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output_stream)); - if (g_bytes_get_size (request->priv->bytes) == 0) { + if (g_bytes_get_size (bytes) == 0) { gchar *data; - g_bytes_unref (request->priv->bytes); + g_bytes_unref (bytes); data = g_strdup_printf ( "<p align='center'>%s</p>", _("The message has no text content.")); /* Takes ownership of the string. */ - request->priv->bytes = g_bytes_new_take ( - data, strlen (data) + 1); + bytes = g_bytes_new_take (data, strlen (data) + 1); } - input_stream = - g_memory_input_stream_new_from_bytes (request->priv->bytes); + if (!use_mime_type) + use_mime_type = g_strdup ("text/html"); - g_simple_async_result_set_op_res_gpointer ( - simple, g_object_ref (input_stream), - (GDestroyNotify) g_object_unref); + if (part_converted_to_utf8 && g_strcmp0 (use_mime_type, "text/html") == 0) { + tmp = g_strconcat (use_mime_type, "; charset=\"UTF-8\"", NULL); + g_free (use_mime_type); + use_mime_type = tmp; + } - g_object_unref (input_stream); - g_object_unref (output_stream); + *out_stream = g_memory_input_stream_new_from_bytes (bytes); + *out_stream_length = g_bytes_get_size (bytes); + *out_mime_type = use_mime_type; + g_object_unref (output_stream); g_object_unref (part_list); g_object_unref (formatter); -} - -static GInputStream * -get_empty_image_stream (void) -{ - GdkPixbuf *pixbuf; - gchar *buffer; - gsize length; - - pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1); - gdk_pixbuf_fill (pixbuf, 0x00000000); /* transparent black */ - gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &length, "png", NULL, NULL); - g_object_unref (pixbuf); + g_bytes_unref (bytes); + g_free (context.uri); - return g_memory_input_stream_new_from_data (buffer, length, g_free); + return TRUE; } -static void -handle_contact_photo_request (GSimpleAsyncResult *simple, - GObject *object, - GCancellable *cancellable) +static gboolean +mail_request_process_contact_photo_sync (EContentRequest *request, + SoupURI *suri, + GHashTable *uri_query, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error) { - EMailRequest *request = E_MAIL_REQUEST (object); EShell *shell; EShellBackend *shell_backend; EMailBackend *mail_backend; @@ -256,227 +318,188 @@ handle_contact_photo_request (GSimpleAsyncResult *simple, const gchar *email_address; const gchar *escaped_string; gchar *unescaped_string; - GError *error = NULL; + gboolean success = FALSE; - /* XXX Is this really the only way to obtain - * the mail session instance from here? */ shell = e_shell_get_default (); shell_backend = e_shell_get_backend_by_name (shell, "mail"); mail_backend = E_MAIL_BACKEND (shell_backend); mail_session = e_mail_backend_get_session (mail_backend); - photo_cache = e_mail_ui_session_get_photo_cache ( - E_MAIL_UI_SESSION (mail_session)); + photo_cache = e_mail_ui_session_get_photo_cache (E_MAIL_UI_SESSION (mail_session)); - request->priv->mime_type = g_strdup ("image/*"); + escaped_string = g_hash_table_lookup (uri_query, "mailaddr"); + if (escaped_string && *escaped_string) { + cia = camel_internet_address_new (); - escaped_string = g_hash_table_lookup ( - request->priv->uri_query, "mailaddr"); - if (escaped_string == NULL || *escaped_string == '\0') - goto exit; + unescaped_string = g_uri_unescape_string (escaped_string, NULL); + camel_address_decode (CAMEL_ADDRESS (cia), unescaped_string); + g_free (unescaped_string); - cia = camel_internet_address_new (); + if (camel_internet_address_get (cia, 0, NULL, &email_address)) + success = e_photo_cache_get_photo_sync ( + photo_cache, email_address, + cancellable, &stream, error); - unescaped_string = g_uri_unescape_string (escaped_string, NULL); - camel_address_decode (CAMEL_ADDRESS (cia), unescaped_string); - g_free (unescaped_string); + g_object_unref (cia); - if (camel_internet_address_get (cia, 0, NULL, &email_address)) - e_photo_cache_get_photo_sync ( - photo_cache, email_address, - cancellable, &stream, &error); + if (success) { + *out_stream = stream; + *out_stream_length = -1; + *out_mime_type = g_strdup ("image/*"); + } + } - g_object_unref (cia); + if (!success) { + GdkPixbuf *pixbuf; + gchar *buffer; + gsize length; - /* Ignore cancellations. */ - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - g_clear_error (&error); - } else if (error != NULL) { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_clear_error (&error); - } + g_clear_error (error); -exit: - if (stream == NULL) - stream = get_empty_image_stream (); + /* Construct empty image stream, to not show "broken image" icon when no contact photo is found */ + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1); + gdk_pixbuf_fill (pixbuf, 0x00000000); /* transparent black */ + gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &length, "png", NULL, NULL); + g_object_unref (pixbuf); - g_simple_async_result_set_op_res_gpointer ( - simple, g_object_ref (stream), - (GDestroyNotify) g_object_unref); + *out_stream = g_memory_input_stream_new_from_data (buffer, length, g_free); + *out_stream_length = length; + *out_mime_type = g_strdup ("image/png"); + } - g_object_unref (stream); + return TRUE; } -static void -mail_request_finalize (GObject *object) +typedef struct _MailIdleData { - EMailRequestPrivate *priv; + EContentRequest *request; + SoupURI *suri; + GHashTable *uri_query; + GObject *requester; + GInputStream **out_stream; + gint64 *out_stream_length; + gchar **out_mime_type; + GCancellable *cancellable; + GError **error; - priv = E_MAIL_REQUEST_GET_PRIVATE (object); + gboolean success; + EFlag *flag; +} MailIdleData; - if (priv->bytes != NULL) - g_bytes_unref (priv->bytes); +static gboolean +process_mail_request_idle_cb (gpointer user_data) +{ + MailIdleData *mid = user_data; - if (priv->uri_query != NULL) - g_hash_table_destroy (priv->uri_query); + g_return_val_if_fail (mid != NULL, FALSE); + g_return_val_if_fail (E_IS_MAIL_REQUEST (mid->request), FALSE); + g_return_val_if_fail (mid->suri != NULL, FALSE); + g_return_val_if_fail (mid->flag != NULL, FALSE); - g_free (priv->mime_type); - g_free (priv->uri_base); - g_free (priv->full_uri); - g_free (priv->ret_mime_type); + mid->success = mail_request_process_mail_sync (mid->request, + mid->suri, mid->uri_query, mid->requester, mid->out_stream, + mid->out_stream_length, mid->out_mime_type, + mid->cancellable, mid->error); - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_mail_request_parent_class)->finalize (object); -} + e_flag_set (mid->flag); -static gboolean -mail_request_check_uri (SoupRequest *request, - SoupURI *uri, - GError **error) -{ - return (strcmp (uri->scheme, "mail") == 0); + return FALSE; } -static void -mail_request_send_async (SoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +static gboolean +e_mail_request_process_sync (EContentRequest *request, + const gchar *uri, + GObject *requester, + GInputStream **out_stream, + gint64 *out_stream_length, + gchar **out_mime_type, + GCancellable *cancellable, + GError **error) { - EMailRequestPrivate *priv; - GSimpleAsyncResult *simple; - SoupURI *uri; + SoupURI *suri; + GHashTable *uri_query; + gboolean success = FALSE; - priv = E_MAIL_REQUEST_GET_PRIVATE (request); + g_return_val_if_fail (E_IS_MAIL_REQUEST (request), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); - uri = soup_request_get_uri (request); + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; - d (printf ("received request for %s\n", soup_uri_to_string (uri, FALSE))); + suri = soup_uri_new (uri); + g_return_val_if_fail (suri != NULL, FALSE); - if (uri->query) { - priv->uri_query = soup_form_decode (uri->query); + if (suri->query) { + uri_query = soup_form_decode (suri->query); } else { - priv->uri_query = NULL; + uri_query = NULL; } - priv->full_uri = soup_uri_to_string (uri, FALSE); - priv->uri_base = g_strdup_printf ( - "%s://%s%s", uri->scheme, uri->host, uri->path); - - simple = g_simple_async_result_new ( - G_OBJECT (request), callback, - user_data, mail_request_send_async); - - g_simple_async_result_set_check_cancellable (simple, cancellable); - - if (g_strcmp0 (uri->host, "contact-photo") == 0) { - e_util_run_simple_async_result_in_thread ( - simple, handle_contact_photo_request, - cancellable); + if (g_strcmp0 (suri->host, "contact-photo") == 0) { + success = mail_request_process_contact_photo_sync (request, suri, uri_query, requester, + out_stream, out_stream_length, out_mime_type, cancellable, error); } else { - /* Process e-mail mail requests in this thread, which should be - * the main/UI thread, because any EMailFormatter can create - * GtkWidget-s, or manipulate with them, which should be always - * done in the main/UI thread. */ - handle_mail_request (simple, G_OBJECT (request), cancellable); - g_simple_async_result_complete_in_idle (simple); - } - - g_object_unref (simple); -} - -static GInputStream * -mail_request_send_finish (SoupRequest *request, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - GInputStream *stream; - - simple = G_SIMPLE_ASYNC_RESULT (result); - stream = g_simple_async_result_get_op_res_gpointer (simple); + MailIdleData mid; + + mid.request = request; + mid.suri = suri; + mid.uri_query = uri_query; + mid.requester = requester; + mid.out_stream = out_stream; + mid.out_stream_length = out_stream_length; + mid.out_mime_type = out_mime_type; + mid.cancellable = cancellable; + mid.error = error; + mid.flag = e_flag_new (); + mid.success = FALSE; + + if (e_util_is_main_thread (NULL)) { + process_mail_request_idle_cb (&mid); + } else { + /* Process e-mail mail requests in the main/UI thread, because + * any EMailFormatter can create GtkWidget-s, or manipulate with + * them, which should be always done in the main/UI thread. */ + g_idle_add_full ( + G_PRIORITY_HIGH_IDLE, + process_mail_request_idle_cb, + &mid, NULL); + + e_flag_wait (mid.flag); + } - /* Reset the stream before passing it back to webkit */ - if (G_IS_SEEKABLE (stream)) - g_seekable_seek ( - G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL); + e_flag_free (mid.flag); - if (stream == NULL) { - /* We must always return something */ - stream = g_memory_input_stream_new (); - } else { - g_object_ref (stream); + success = mid.success; } - return stream; -} - -static goffset -mail_request_get_content_length (SoupRequest *request) -{ - EMailRequestPrivate *priv; - goffset content_length = -1; /* -1 means unknown */ + if (uri_query) + g_hash_table_destroy (uri_query); + soup_uri_free (suri); - priv = E_MAIL_REQUEST_GET_PRIVATE (request); - - if (priv->bytes != NULL) - content_length = g_bytes_get_size (priv->bytes); - - return content_length; + return success; } -static const gchar * -mail_request_get_content_type (SoupRequest *request) +static void +e_mail_request_content_request_init (EContentRequestInterface *iface) { - EMailRequestPrivate *priv; - gchar *mime_type; - - priv = E_MAIL_REQUEST_GET_PRIVATE (request); - - if (priv->mime_type != NULL) { - mime_type = g_strdup (priv->mime_type); - } else { - mime_type = g_strdup ("text/html"); - } - - if (g_strcmp0 (mime_type, "text/html") == 0 && - priv->part_converted_to_utf8) { - priv->ret_mime_type = g_strconcat ( - mime_type, "; charset=\"UTF-8\"", NULL); - g_free (mime_type); - } else { - priv->ret_mime_type = mime_type; - } - - d (printf ("Content-Type: %s\n", priv->ret_mime_type)); - - return priv->ret_mime_type; + iface->can_process_uri = e_mail_request_can_process_uri; + iface->process_sync = e_mail_request_process_sync; } static void e_mail_request_class_init (EMailRequestClass *class) { - GObjectClass *object_class; - SoupRequestClass *request_class; - g_type_class_add_private (class, sizeof (EMailRequestPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = mail_request_finalize; - - request_class = SOUP_REQUEST_CLASS (class); - request_class->schemes = data_schemes; - request_class->send_async = mail_request_send_async; - request_class->send_finish = mail_request_send_finish; - request_class->get_content_type = mail_request_get_content_type; - request_class->get_content_length = mail_request_get_content_length; - request_class->check_uri = mail_request_check_uri; } static void e_mail_request_init (EMailRequest *request) { - request->priv = E_MAIL_REQUEST_GET_PRIVATE (request); - request->priv->part_converted_to_utf8 = FALSE; + request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_MAIL_REQUEST, EMailRequestPrivate); } +EContentRequest * +e_mail_request_new (void) +{ + return g_object_new (E_TYPE_MAIL_REQUEST, NULL); +} diff --git a/mail/e-mail-request.h b/mail/e-mail-request.h index c85266e..cd35992 100644 --- a/mail/e-mail-request.h +++ b/mail/e-mail-request.h @@ -18,10 +18,7 @@ #ifndef E_MAIL_REQUEST_H #define E_MAIL_REQUEST_H -#define LIBSOUP_USE_UNSTABLE_REQUEST_API - -#include <libsoup/soup.h> -#include <libsoup/soup-request.h> +#include <e-util/e-util.h> /* Standard GObject macros */ #define E_TYPE_MAIL_REQUEST \ @@ -49,15 +46,17 @@ typedef struct _EMailRequestClass EMailRequestClass; typedef struct _EMailRequestPrivate EMailRequestPrivate; struct _EMailRequest { - SoupRequest parent; + GObject parent; EMailRequestPrivate *priv; }; struct _EMailRequestClass { - SoupRequestClass parent; + GObjectClass parent; }; GType e_mail_request_get_type (void) G_GNUC_CONST; +EContentRequest * + e_mail_request_new (void); G_END_DECLS diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c index a983db5..2a68541 100644 --- a/mail/em-composer-utils.c +++ b/mail/em-composer-utils.c @@ -534,7 +534,7 @@ composer_presend_check_unwanted_html (EMsgComposer *composer, { EDestination **recipients; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; EComposerHeaderTable *table; GSettings *settings; gboolean check_passed = TRUE; @@ -546,8 +546,8 @@ composer_presend_check_unwanted_html (EMsgComposer *composer, settings = e_util_ref_settings ("org.gnome.evolution.mail"); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - html_mode = e_html_editor_view_get_html_mode (view); + cnt_editor = e_html_editor_get_content_editor (editor); + html_mode = e_content_editor_get_html_mode (cnt_editor); table = e_msg_composer_get_header_table (composer); recipients = e_composer_header_table_get_destinations (table); @@ -681,11 +681,12 @@ exit: if (set_changed) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; editor = e_msg_composer_get_editor (async_context->composer); - view = e_html_editor_get_view (editor); - e_html_editor_view_set_changed (view, TRUE); + cnt_editor = e_html_editor_get_content_editor (editor); + + e_content_editor_set_changed (cnt_editor, TRUE); gtk_window_present (GTK_WINDOW (async_context->composer)); } @@ -800,14 +801,14 @@ static void composer_set_no_change (EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; g_return_if_fail (composer != NULL); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_view_set_changed (view, FALSE); + e_content_editor_set_changed (cnt_editor, FALSE); } /* delete original messages from Outbox folder */ @@ -853,13 +854,13 @@ composer_save_to_drafts_complete (GObject *source_object, EActivity *activity; AsyncContext *async_context; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GError *local_error = NULL; async_context = (AsyncContext *) user_data; editor = e_msg_composer_get_editor (async_context->composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); /* We don't really care if this failed. If something other than * cancellation happened, emit a runtime warning so the error is @@ -870,14 +871,13 @@ composer_save_to_drafts_complete (GObject *source_object, activity = async_context->activity; if (e_activity_handle_cancellation (activity, local_error)) { - e_html_editor_view_set_changed (view, TRUE); + e_content_editor_set_changed (cnt_editor, TRUE); g_error_free (local_error); } else if (local_error != NULL) { - e_html_editor_view_set_changed (view, TRUE); + e_content_editor_set_changed (cnt_editor, TRUE); g_warning ("%s", local_error->message); g_error_free (local_error); - } else e_activity_set_state (activity, E_ACTIVITY_COMPLETED); @@ -907,14 +907,14 @@ composer_save_to_drafts_cleanup (GObject *source_object, EAlertSink *alert_sink; GCancellable *cancellable; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; AsyncContext *async_context; GError *local_error = NULL; async_context = (AsyncContext *) user_data; editor = e_msg_composer_get_editor (async_context->composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); activity = async_context->activity; alert_sink = e_activity_get_alert_sink (activity); @@ -926,7 +926,7 @@ composer_save_to_drafts_cleanup (GObject *source_object, if (e_activity_handle_cancellation (activity, local_error)) { g_warn_if_fail (async_context->message_uid == NULL); - e_html_editor_view_set_changed (view, TRUE); + e_content_editor_set_changed (cnt_editor, TRUE); async_context_free (async_context); g_error_free (local_error); return; @@ -944,7 +944,7 @@ composer_save_to_drafts_cleanup (GObject *source_object, GTK_WINDOW (async_context->composer), "mail:ask-default-drafts", local_error->message, NULL); if (response != GTK_RESPONSE_YES) { - e_html_editor_view_set_changed (view, TRUE); + e_content_editor_set_changed (cnt_editor, TRUE); async_context_free (async_context); } else { composer_save_to_drafts_append_mail (async_context, NULL); @@ -958,7 +958,7 @@ composer_save_to_drafts_cleanup (GObject *source_object, alert_sink, "mail-composer:save-to-drafts-error", local_error->message, NULL); - e_html_editor_view_set_changed (view, TRUE); + e_content_editor_set_changed (cnt_editor, TRUE); async_context_free (async_context); g_error_free (local_error); return; @@ -1023,7 +1023,7 @@ composer_save_to_drafts_got_folder (GObject *source_object, EActivity *activity; CamelFolder *drafts_folder; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; AsyncContext *async_context; GError *local_error = NULL; @@ -1032,7 +1032,7 @@ composer_save_to_drafts_got_folder (GObject *source_object, activity = async_context->activity; editor = e_msg_composer_get_editor (async_context->composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); drafts_folder = e_mail_session_uri_to_folder_finish ( E_MAIL_SESSION (source_object), result, &local_error); @@ -1043,7 +1043,7 @@ composer_save_to_drafts_got_folder (GObject *source_object, ((drafts_folder == NULL) && (local_error != NULL))); if (e_activity_handle_cancellation (activity, local_error)) { - e_html_editor_view_set_changed (view, TRUE); + e_content_editor_set_changed (cnt_editor, TRUE); async_context_free (async_context); g_error_free (local_error); return; @@ -1061,7 +1061,7 @@ composer_save_to_drafts_got_folder (GObject *source_object, g_error_free (local_error); if (response != GTK_RESPONSE_YES) { - e_html_editor_view_set_changed (view, TRUE); + e_content_editor_set_changed (cnt_editor, TRUE); async_context_free (async_context); return; } @@ -1316,20 +1316,17 @@ em_utils_composer_print_cb (EMsgComposer *composer, /* Composing messages... */ -static EMsgComposer * -create_new_composer (EShell *shell, - const gchar *subject, - CamelFolder *folder) +static void +set_up_new_composer (EMsgComposer *composer, + const gchar *subject, + CamelFolder *folder) { - EMsgComposer *composer; EClientCache *client_cache; ESourceRegistry *registry; EComposerHeaderTable *table; ESource *source = NULL; gchar *identity = NULL; - composer = e_msg_composer_new (shell); - table = e_msg_composer_get_header_table (composer); client_cache = e_composer_header_table_ref_client_cache (table); @@ -1360,50 +1357,36 @@ create_new_composer (EShell *shell, e_composer_header_table_set_subject (table, subject); e_composer_header_table_set_identity_uid (table, identity); - em_utils_apply_send_account_override_to_composer (composer, shell, folder); + em_utils_apply_send_account_override_to_composer (composer, folder); g_free (identity); g_object_unref (client_cache); g_object_unref (registry); - - return composer; } /** * em_utils_compose_new_message: - * @shell: an #EShell - * @folder: a #CamelFolder, or %NULL + * @composer: an #EMsgComposer + * @folder: (nullable): a #CamelFolder, or %NULL * - * Opens a new composer window as a child window of @parent's toplevel - * window. + * Sets up a new @composer window. * - * Returns: the resulting #EMsgComposer + * Since: 3.22 **/ -EMsgComposer * -em_utils_compose_new_message (EShell *shell, +void +em_utils_compose_new_message (EMsgComposer *composer, CamelFolder *folder) { - EMsgComposer *composer; - EHTMLEditor *editor; - EHTMLEditorView *view; - - g_return_val_if_fail (E_IS_SHELL (shell), NULL); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); if (folder != NULL) - g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); + g_return_if_fail (CAMEL_IS_FOLDER (folder)); - composer = create_new_composer (shell, "", folder); + set_up_new_composer (composer, "", folder); composer_set_no_change (composer); - editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - - e_html_editor_view_set_content_is_new_message (view, TRUE); - gtk_widget_show (GTK_WIDGET (composer)); - - return composer; } static CamelMimeMessage * @@ -1483,37 +1466,50 @@ em_utils_get_composer_recipients_as_message (EMsgComposer *composer) return message; } -/** - * em_utils_compose_new_message_with_mailto: - * @shell: an #EShell - * @mailto: a mailto URL - * @folder: a #CamelFolder, or %NULL - * - * Opens a new composer window as a child window of @parent's toplevel - * window. If @mailto is non-NULL, the composer fields will be filled in - * according to the values in the mailto URL. - **/ -EMsgComposer * -em_utils_compose_new_message_with_mailto (EShell *shell, - const gchar *mailto, - CamelFolder *folder) +typedef struct _CreateComposerData { + gchar *mailto; + CamelFolder *folder; +} CreateComposerData; + +static void +create_composer_data_free (gpointer ptr) +{ + CreateComposerData *ccd = ptr; + + if (ccd) { + g_clear_object (&ccd->folder); + g_free (ccd->mailto); + g_free (ccd); + } +} + +static void +msg_composer_created_with_mailto_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { + CreateComposerData *ccd = user_data; EMsgComposer *composer; EComposerHeaderTable *table; EClientCache *client_cache; ESourceRegistry *registry; + GError *error = NULL; - g_return_val_if_fail (E_IS_SHELL (shell), NULL); + g_return_if_fail (ccd != NULL); - if (folder != NULL) - g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + create_composer_data_free (ccd); - if (mailto != NULL) - composer = e_msg_composer_new_from_url (shell, mailto); - else - composer = e_msg_composer_new (shell); + return; + } + + if (ccd->mailto) + e_msg_composer_setup_from_url (composer, ccd->mailto); - em_utils_apply_send_account_override_to_composer (composer, shell, folder); + em_utils_apply_send_account_override_to_composer (composer, ccd->folder); table = e_msg_composer_get_header_table (composer); @@ -1525,11 +1521,11 @@ em_utils_compose_new_message_with_mailto (EShell *shell, /* If a CamelFolder was given, we need to backtrack and find * the corresponding ESource with a Mail Identity extension. */ - if (folder != NULL) { + if (ccd->folder) { ESource *source; CamelStore *store; - store = camel_folder_get_parent_store (folder); + store = camel_folder_get_parent_store (ccd->folder); source = em_utils_ref_mail_identity_for_store (registry, store); if (source != NULL) { @@ -1544,7 +1540,36 @@ em_utils_compose_new_message_with_mailto (EShell *shell, gtk_window_present (GTK_WINDOW (composer)); - return composer; + create_composer_data_free (ccd); +} + +/** + * em_utils_compose_new_message_with_mailto: + * @shell: an #EShell + * @mailto: a mailto URL + * @folder: a #CamelFolder, or %NULL + * + * Opens a new composer window as a child window of @parent's toplevel + * window. If @mailto is non-NULL, the composer fields will be filled in + * according to the values in the mailto URL. + **/ +void +em_utils_compose_new_message_with_mailto (EShell *shell, + const gchar *mailto, + CamelFolder *folder) +{ + CreateComposerData *ccd; + + g_return_if_fail (E_IS_SHELL (shell)); + + if (folder != NULL) + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + + ccd = g_new0 (CreateComposerData, 1); + ccd->folder = folder ? g_object_ref (folder) : NULL; + ccd->mailto = g_strdup (mailto); + + e_msg_composer_new (shell, msg_composer_created_with_mailto_cb, ccd); } static gboolean @@ -1749,22 +1774,22 @@ quoting_text (QuotingTextEnum type) /** * em_utils_edit_message: - * @shell: an #EShell + * @composer: an #EMsgComposer * @folder: a #CamelFolder * @message: a #CamelMimeMessage * @message_uid: UID of @message, or %NULL * - * Opens a composer filled in with the headers/mime-parts/etc of - * @message. + * Sets up the @composer with the headers/mime-parts/etc of the @message. + * + * Since: 3.22 **/ -EMsgComposer * -em_utils_edit_message (EShell *shell, +void +em_utils_edit_message (EMsgComposer *composer, CamelFolder *folder, CamelMimeMessage *message, const gchar *message_uid, gboolean keep_signature) { - EMsgComposer *composer; ESourceRegistry *registry; ESource *source; gboolean folder_is_sent; @@ -1773,12 +1798,12 @@ em_utils_edit_message (EShell *shell, gboolean folder_is_templates; gchar *override_identity_uid = NULL; - g_return_val_if_fail (E_IS_SHELL (shell), NULL); - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); if (folder) - g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); + g_return_if_fail (CAMEL_IS_FOLDER (folder)); - registry = e_shell_get_registry (shell); + registry = e_shell_get_registry (e_msg_composer_get_shell (composer)); if (folder) { folder_is_sent = em_utils_folder_is_sent (registry, folder); @@ -1830,7 +1855,7 @@ em_utils_edit_message (EShell *shell, } } - source = em_utils_check_send_account_override (shell, message, folder); + source = em_utils_check_send_account_override (e_msg_composer_get_shell (composer), message, folder); if (source) { g_free (override_identity_uid); override_identity_uid = e_source_dup_uid (source); @@ -1838,7 +1863,7 @@ em_utils_edit_message (EShell *shell, } } - composer = e_msg_composer_new_with_message (shell, message, keep_signature, override_identity_uid, NULL); + e_msg_composer_setup_with_message (composer, message, keep_signature, override_identity_uid, NULL); g_free (override_identity_uid); @@ -1881,8 +1906,6 @@ em_utils_edit_message (EShell *shell, composer_set_no_change (composer); gtk_widget_show (GTK_WIDGET (composer)); - - return composer; } static void @@ -2098,39 +2121,36 @@ setup_forward_attached_callbacks (EMsgComposer *composer, (GDestroyNotify) forward_data_free); } -static EMsgComposer * -forward_non_attached (EMailBackend *backend, +static void +forward_non_attached (EMsgComposer *composer, CamelFolder *folder, const gchar *uid, CamelMimeMessage *message, EMailForwardStyle style) { - EMsgComposer *composer = NULL; - EMailSession *session; - EShell *shell; + CamelSession *session; gchar *text, *forward; guint32 validity_found = 0; guint32 flags; + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + session = e_msg_composer_ref_session (composer); + flags = E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS | E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG; if (style == E_MAIL_FORWARD_STYLE_QUOTED) flags |= E_MAIL_FORMATTER_QUOTE_FLAG_CITE; - session = e_mail_backend_get_session (backend); - shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); - forward = quoting_text (QUOTING_FORWARD); - text = em_utils_message_to_html ( - CAMEL_SESSION (session), message, - forward, flags, NULL, NULL, NULL, &validity_found); + text = em_utils_message_to_html (session, message, forward, flags, NULL, NULL, NULL, &validity_found); if (text != NULL) { CamelDataWrapper *content; gchar *subject; subject = mail_tool_generate_forward_subject (message); - composer = create_new_composer (shell, subject, folder); + set_up_new_composer (composer, subject, folder); g_free (subject); content = camel_medium_get_content (CAMEL_MEDIUM (message)); @@ -2157,26 +2177,24 @@ forward_non_attached (EMailBackend *backend, g_free (tmp_message_uid); } - emu_update_composers_security ( - composer, validity_found); + emu_update_composers_security (composer, validity_found); composer_set_no_change (composer); gtk_widget_show (GTK_WIDGET (composer)); g_free (text); } + g_clear_object (&session); g_free (forward); - - return composer; } /** * em_utils_forward_message: - * @backend: an #EMailBackend + * @composer: an #EMsgComposer * @message: a #CamelMimeMessage to forward * @style: the forward style to use - * @folder: a #CamelFolder, or %NULL - * @uid: the UID of %message, or %NULL + * @folder: (nullable): a #CamelFolder, or %NULL + * @uid: (nullable): the UID of %message, or %NULL * * Forwards @message in the given @style. * @@ -2194,8 +2212,8 @@ forward_non_attached (EMailBackend *backend, * in its own composer window in 'quoted' form (each line starting with * a "> "). **/ -EMsgComposer * -em_utils_forward_message (EMailBackend *backend, +void +em_utils_forward_message (EMsgComposer *composer, CamelMimeMessage *message, EMailForwardStyle style, CamelFolder *folder, @@ -2203,10 +2221,9 @@ em_utils_forward_message (EMailBackend *backend, { CamelMimePart *part; gchar *subject; - EMsgComposer *composer = NULL; - g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL); - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); switch (style) { case E_MAIL_FORWARD_STYLE_ATTACHED: @@ -2214,8 +2231,7 @@ em_utils_forward_message (EMailBackend *backend, part = mail_tool_make_message_attachment (message); subject = mail_tool_generate_forward_subject (message); - composer = em_utils_forward_attachment ( - backend, part, subject, NULL, NULL); + em_utils_forward_attachment (composer, part, subject, NULL, NULL); g_object_unref (part); g_free (subject); @@ -2223,34 +2239,27 @@ em_utils_forward_message (EMailBackend *backend, case E_MAIL_FORWARD_STYLE_INLINE: case E_MAIL_FORWARD_STYLE_QUOTED: - composer = forward_non_attached ( - backend, folder, uid, message, style); + forward_non_attached (composer, folder, uid, message, style); break; } - - return composer; } -EMsgComposer * -em_utils_forward_attachment (EMailBackend *backend, +void +em_utils_forward_attachment (EMsgComposer *composer, CamelMimePart *part, const gchar *subject, CamelFolder *folder, GPtrArray *uids) { - EShell *shell; CamelDataWrapper *content; - EMsgComposer *composer; - g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL); - g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_PART (part)); if (folder != NULL) - g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); - - shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); + g_return_if_fail (CAMEL_IS_FOLDER (folder)); - composer = create_new_composer (shell, subject, folder); + set_up_new_composer (composer, subject, folder); e_msg_composer_attach (composer, part); @@ -2291,8 +2300,6 @@ em_utils_forward_attachment (EMailBackend *backend, composer_set_no_change (composer); gtk_widget_show (GTK_WIDGET (composer)); - - return composer; } static gint @@ -2368,18 +2375,30 @@ sort_sources_by_ui (GList **psources, g_hash_table_destroy (uids_order); } -/* Redirecting messages... */ - -static EMsgComposer * -redirect_get_composer (EShell *shell, - CamelMimeMessage *message) +/** + * em_utils_redirect_message: + * @composer: an #EMsgComposer + * @message: message to redirect + * + * Sets up the @composer to redirect @message (Note: only headers will be + * editable). Adds Resent-From/Resent-To/etc headers. + * + * Since: 3.22 + **/ +void +em_utils_redirect_message (EMsgComposer *composer, + CamelMimeMessage *message) { - EMsgComposer *composer; ESourceRegistry *registry; - CamelMedium *medium; ESource *source; + EShell *shell; + CamelMedium *medium; gchar *identity_uid = NULL; + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + shell = e_msg_composer_get_shell (composer); medium = CAMEL_MEDIUM (message); /* QMail will refuse to send a message if it finds one of @@ -2407,40 +2426,13 @@ redirect_get_composer (EShell *shell, g_object_unref (source); } - composer = e_msg_composer_new_redirect ( - shell, message, identity_uid, NULL); + e_msg_composer_setup_redirect (composer, message, identity_uid, NULL); g_free (identity_uid); - return composer; -} - -/** - * em_utils_redirect_message: - * @shell: an #EShell - * @message: message to redirect - * - * Opens a composer to redirect @message (Note: only headers will be - * editable). Adds Resent-From/Resent-To/etc headers. - * - * Returns: the resulting #EMsgComposer - **/ -EMsgComposer * -em_utils_redirect_message (EShell *shell, - CamelMimeMessage *message) -{ - EMsgComposer *composer; - - g_return_val_if_fail (E_IS_SHELL (shell), NULL); - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); - - composer = redirect_get_composer (shell, message); - gtk_widget_show (GTK_WIDGET (composer)); composer_set_no_change (composer); - - return composer; } /* Replying to messages... */ @@ -2480,33 +2472,30 @@ em_utils_camel_address_to_destination (CamelInternetAddress *iaddr) return destv; } -static EMsgComposer * -reply_get_composer (EShell *shell, - CamelMimeMessage *message, - const gchar *identity_uid, - CamelInternetAddress *to, - CamelInternetAddress *cc, - CamelFolder *folder, - const gchar *message_uid, - CamelNNTPAddress *postto) +static void +reply_setup_composer (EMsgComposer *composer, + CamelMimeMessage *message, + const gchar *identity_uid, + CamelInternetAddress *to, + CamelInternetAddress *cc, + CamelFolder *folder, + const gchar *message_uid, + CamelNNTPAddress *postto) { gchar *message_id, *references; EDestination **tov, **ccv; - EMsgComposer *composer; EComposerHeaderTable *table; CamelMedium *medium; gchar *subject; - g_return_val_if_fail (E_IS_SHELL (shell), NULL); - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); if (to != NULL) - g_return_val_if_fail (CAMEL_IS_INTERNET_ADDRESS (to), NULL); + g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to)); if (cc != NULL) - g_return_val_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc), NULL); - - composer = e_msg_composer_new (shell); + g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc)); /* construct the tov/ccv */ tov = em_utils_camel_address_to_destination (to); @@ -2605,8 +2594,6 @@ reply_get_composer (EShell *shell, g_free (message_id); g_free (references); - - return composer; } static gboolean @@ -3327,8 +3314,8 @@ emcu_folder_is_inbox (CamelFolder *folder) * @folder and @message_uid may be supplied in order to update the message * flags once it has been replied to. **/ -EMsgComposer * -em_utils_reply_to_message (EShell *shell, +void +em_utils_reply_to_message (EMsgComposer *composer, CamelMimeMessage *message, CamelFolder *folder, const gchar *message_uid, @@ -3340,19 +3327,19 @@ em_utils_reply_to_message (EShell *shell, ESourceRegistry *registry; CamelInternetAddress *to, *cc; CamelNNTPAddress *postto = NULL; - EMsgComposer *composer; + EShell *shell; ESourceMailCompositionReplyStyle prefer_reply_style = E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT; ESource *source; gchar *identity_uid = NULL; - const gchar *evo_source_header; guint32 flags; - g_return_val_if_fail (E_IS_SHELL (shell), NULL); - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); to = camel_internet_address_new (); cc = camel_internet_address_new (); + shell = e_msg_composer_get_shell (composer); registry = e_shell_get_registry (shell); /* This returns a new ESource reference. */ @@ -3411,8 +3398,7 @@ em_utils_reply_to_message (EShell *shell, break; } - composer = reply_get_composer ( - shell, message, identity_uid, to, cc, folder, message_uid, postto); + reply_setup_composer (composer, message, identity_uid, to, cc, folder, message_uid, postto); e_msg_composer_add_message_attachments (composer, message, TRUE); if (postto) @@ -3420,18 +3406,6 @@ em_utils_reply_to_message (EShell *shell, g_object_unref (to); g_object_unref (cc); - evo_source_header = camel_medium_get_header ( - CAMEL_MEDIUM (message), "X-Evolution-Content-Source"); - if (g_strcmp0 (evo_source_header, "selection") == 0) { - EHTMLEditor *editor; - EHTMLEditorView *view; - - editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); - - e_html_editor_view_set_is_message_from_selection (view, TRUE); - } - /* If there was no send-account override */ if (!identity_uid) { EComposerHeaderTable *header_table; @@ -3488,15 +3462,13 @@ em_utils_reply_to_message (EShell *shell, } /* because some reply types can change recipients after the composer is populated */ - em_utils_apply_send_account_override_to_composer (composer, shell, folder); + em_utils_apply_send_account_override_to_composer (composer, folder); composer_set_no_change (composer); gtk_widget_show (GTK_WIDGET (composer)); g_free (identity_uid); - - return composer; } static void @@ -3684,15 +3656,16 @@ em_utils_check_send_account_override (EShell *shell, void em_utils_apply_send_account_override_to_composer (EMsgComposer *composer, - EShell *shell, CamelFolder *folder) { CamelMimeMessage *message; EComposerHeaderTable *header_table; + EShell *shell; ESource *source; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + shell = e_msg_composer_get_shell (composer); message = em_utils_get_composer_recipients_as_message (composer); source = em_utils_check_send_account_override (shell, message, folder); g_clear_object (&message); diff --git a/mail/em-composer-utils.h b/mail/em-composer-utils.h index fc5c0aa..891f68a 100644 --- a/mail/em-composer-utils.h +++ b/mail/em-composer-utils.h @@ -33,28 +33,28 @@ G_BEGIN_DECLS -EMsgComposer * em_utils_compose_new_message (EShell *shell, +void em_utils_compose_new_message (EMsgComposer *composer, CamelFolder *folder); -EMsgComposer * em_utils_compose_new_message_with_mailto +void em_utils_compose_new_message_with_mailto (EShell *shell, const gchar *mailto, CamelFolder *folder); -EMsgComposer * em_utils_edit_message (EShell *shell, +void em_utils_edit_message (EMsgComposer *composer, CamelFolder *folder, CamelMimeMessage *message, const gchar *message_uid, gboolean keep_signature); -EMsgComposer * em_utils_forward_message (EMailBackend *backend, +void em_utils_forward_message (EMsgComposer *composer, CamelMimeMessage *message, EMailForwardStyle style, CamelFolder *folder, const gchar *uid); -EMsgComposer * em_utils_forward_attachment (EMailBackend *backend, +void em_utils_forward_attachment (EMsgComposer *composer, CamelMimePart *part, const gchar *subject, CamelFolder *folder, GPtrArray *uids); -EMsgComposer * em_utils_redirect_message (EShell *shell, +void em_utils_redirect_message (EMsgComposer *composer, CamelMimeMessage *message); gchar * em_utils_construct_composer_text (CamelSession *session, @@ -69,7 +69,7 @@ void em_utils_get_reply_all (ESourceRegistry *registry, CamelInternetAddress *to, CamelInternetAddress *cc, CamelNNTPAddress *postto); -EMsgComposer * em_utils_reply_to_message (EShell *shell, +void em_utils_reply_to_message (EMsgComposer *composer, CamelMimeMessage *message, CamelFolder *folder, const gchar *message_uid, @@ -92,7 +92,6 @@ ESource * em_utils_check_send_account_override CamelFolder *folder); void em_utils_apply_send_account_override_to_composer (EMsgComposer *composer, - EShell *shell, CamelFolder *folder); G_END_DECLS diff --git a/modules/Makefile.am b/modules/Makefile.am index 5dbe228..096146b 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -51,7 +51,8 @@ SUBDIRS = \ settings \ startup-wizard \ vcard-inline \ - web-inspector \ + webkit-editor \ + webkit-inspector \ $(BOGOFILTER_DIR) \ $(SPAMASSASSIN_DIR) \ $(TNEF_ATTACHMENT_DIR) \ diff --git a/modules/addressbook/eab-composer-util.c b/modules/addressbook/eab-composer-util.c index b2d79de..94e8aaf 100644 --- a/modules/addressbook/eab-composer-util.c +++ b/modules/addressbook/eab-composer-util.c @@ -29,28 +29,156 @@ #include "addressbook/util/eab-book-util.h" #include "addressbook/gui/widgets/eab-gui-util.h" +static const gchar * +get_email (EContact *contact, + EContactField field_id, + gchar **to_free) +{ + gchar *name = NULL, *mail = NULL; + const gchar *value = e_contact_get_const (contact, field_id); + + *to_free = NULL; + + if (eab_parse_qp_email (value, &name, &mail)) { + *to_free = g_strdup_printf ("%s <%s>", name, mail); + value = *to_free; + } + + g_free (name); + g_free (mail); + + return value; +} + +typedef struct _CreateComposerData { + EDestination **to_destinations; + EDestination **bcc_destinations; + GSList *attachment_destinations; +} CreateComposerData; + +static void +eab_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateComposerData *ccd = user_data; + EComposerHeaderTable *table; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (ccd != NULL); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + table = e_msg_composer_get_header_table (composer); + + if (ccd->to_destinations) + e_composer_header_table_set_destinations_to (table, ccd->to_destinations); + + if (ccd->bcc_destinations) + e_composer_header_table_set_destinations_bcc (table, ccd->bcc_destinations); + + if (ccd->attachment_destinations) { + CamelMimePart *attachment; + GSList *contacts, *iter; + gchar *data; + + attachment = camel_mime_part_new (); + + contacts = g_slist_copy (ccd->attachment_destinations); + for (iter = contacts; iter != NULL; iter = iter->next) + iter->data = e_destination_get_contact (iter->data); + data = eab_contact_list_to_string (contacts); + g_slist_free (contacts); + + camel_mime_part_set_content (attachment, data, strlen (data), "text/x-vcard"); + + if (ccd->attachment_destinations->next != NULL) { + camel_mime_part_set_description (attachment, _("Multiple vCards")); + } else { + EContact *contact; + const gchar *file_as; + gchar *description; + + contact = e_destination_get_contact (ccd->attachment_destinations->data); + file_as = e_contact_get_const (contact, E_CONTACT_FILE_AS); + description = g_strdup_printf (_("vCard for %s"), file_as); + camel_mime_part_set_description (attachment, description); + g_free (description); + } + + camel_mime_part_set_disposition (attachment, "attachment"); + + e_msg_composer_attach (composer, attachment); + g_object_unref (attachment); + + if (ccd->attachment_destinations->next != NULL) { + e_composer_header_table_set_subject (table, _("Contact information")); + } else { + EContact *contact; + gchar *tempstr; + const gchar *tempstr2; + gchar *tempfree = NULL; + + contact = e_destination_get_contact (ccd->attachment_destinations->data); + tempstr2 = e_contact_get_const (contact, E_CONTACT_FILE_AS); + if (!tempstr2 || !*tempstr2) + tempstr2 = e_contact_get_const (contact, E_CONTACT_FULL_NAME); + if (!tempstr2 || !*tempstr2) + tempstr2 = e_contact_get_const (contact, E_CONTACT_ORG); + if (!tempstr2 || !*tempstr2) { + g_free (tempfree); + tempstr2 = get_email (contact, E_CONTACT_EMAIL_1, &tempfree); + } + if (!tempstr2 || !*tempstr2) { + g_free (tempfree); + tempstr2 = get_email (contact, E_CONTACT_EMAIL_2, &tempfree); + } + if (!tempstr2 || !*tempstr2) { + g_free (tempfree); + tempstr2 = get_email (contact, E_CONTACT_EMAIL_3, &tempfree); + } + + if (!tempstr2 || !*tempstr2) + tempstr = g_strdup_printf (_("Contact information")); + else + tempstr = g_strdup_printf (_("Contact information for %s"), tempstr2); + + e_composer_header_table_set_subject (table, tempstr); + + g_free (tempstr); + g_free (tempfree); + } + } + + gtk_widget_show (GTK_WIDGET (composer)); + } + + if (ccd->to_destinations) + e_destination_freev (ccd->to_destinations); + if (ccd->bcc_destinations) + e_destination_freev (ccd->bcc_destinations); + g_slist_free_full (ccd->attachment_destinations, g_object_unref); + + g_free (ccd); +} + void eab_send_as_to (EShell *shell, GSList *destinations) { - EMsgComposer *composer; - EComposerHeaderTable *table; GPtrArray *to_array; GPtrArray *bcc_array; - - union { - gpointer *pdata; - EDestination **destinations; - } convert; + CreateComposerData *ccd; g_return_if_fail (E_IS_SHELL (shell)); if (destinations == NULL) return; - composer = e_msg_composer_new (shell); - table = e_msg_composer_get_header_table (composer); - to_array = g_ptr_array_new (); bcc_array = g_ptr_array_new (); @@ -60,11 +188,11 @@ eab_send_as_to (EShell *shell, if (e_destination_is_evolution_list (destination)) { if (e_destination_list_show_addresses (destination)) - g_ptr_array_add (to_array, destination); + g_ptr_array_add (to_array, e_destination_copy (destination)); else - g_ptr_array_add (bcc_array, destination); + g_ptr_array_add (bcc_array, e_destination_copy (destination)); } else - g_ptr_array_add (to_array, destination); + g_ptr_array_add (to_array, e_destination_copy (destination)); destinations = g_slist_next (destinations); } @@ -73,133 +201,28 @@ eab_send_as_to (EShell *shell, g_ptr_array_add (to_array, NULL); g_ptr_array_add (bcc_array, NULL); - /* XXX Acrobatics like this make me question whether NULL-terminated - * arrays are really the best argument type for passing a list of - * destinations to the header table. */ - - /* Set "To" destinations. */ - convert.pdata = to_array->pdata; - e_composer_header_table_set_destinations_to ( - table, convert.destinations); - g_ptr_array_free (to_array, FALSE); - - /* Add "Bcc" destinations. */ - convert.pdata = bcc_array->pdata; - e_composer_header_table_add_destinations_bcc ( - table, convert.destinations); - g_ptr_array_free (bcc_array, FALSE); + ccd = g_new0 (CreateComposerData, 1); + ccd->to_destinations = (EDestination **) g_ptr_array_free (to_array, FALSE); + ccd->bcc_destinations = (EDestination **) g_ptr_array_free (bcc_array, FALSE); + ccd->attachment_destinations = NULL; - gtk_widget_show (GTK_WIDGET (composer)); -} - -static const gchar * -get_email (EContact *contact, - EContactField field_id, - gchar **to_free) -{ - gchar *name = NULL, *mail = NULL; - const gchar *value = e_contact_get_const (contact, field_id); - - *to_free = NULL; - - if (eab_parse_qp_email (value, &name, &mail)) { - *to_free = g_strdup_printf ("%s <%s>", name, mail); - value = *to_free; - } - - g_free (name); - g_free (mail); - - return value; + e_msg_composer_new (shell, eab_composer_created_cb, ccd); } void eab_send_as_attachment (EShell *shell, GSList *destinations) { - EMsgComposer *composer; - EComposerHeaderTable *table; - CamelMimePart *attachment; - GSList *contacts, *iter; - gchar *data; + CreateComposerData *ccd; g_return_if_fail (E_IS_SHELL (shell)); if (destinations == NULL) return; - composer = e_msg_composer_new (shell); - table = e_msg_composer_get_header_table (composer); - - attachment = camel_mime_part_new (); - - contacts = g_slist_copy (destinations); - for (iter = contacts; iter != NULL; iter = iter->next) - iter->data = e_destination_get_contact (iter->data); - data = eab_contact_list_to_string (contacts); - g_slist_free (contacts); - - camel_mime_part_set_content ( - attachment, data, strlen (data), "text/x-vcard"); - - if (destinations->next != NULL) - camel_mime_part_set_description ( - attachment, _("Multiple vCards")); - else { - EContact *contact; - const gchar *file_as; - gchar *description; - - contact = e_destination_get_contact (destinations->data); - file_as = e_contact_get_const (contact, E_CONTACT_FILE_AS); - description = g_strdup_printf (_("vCard for %s"), file_as); - camel_mime_part_set_description (attachment, description); - g_free (description); - } - - camel_mime_part_set_disposition (attachment, "attachment"); - - e_msg_composer_attach (composer, attachment); - g_object_unref (attachment); - - if (destinations->next != NULL) - e_composer_header_table_set_subject ( - table, _("Contact information")); - else { - EContact *contact; - gchar *tempstr; - const gchar *tempstr2; - gchar *tempfree = NULL; - - contact = e_destination_get_contact (destinations->data); - tempstr2 = e_contact_get_const (contact, E_CONTACT_FILE_AS); - if (!tempstr2 || !*tempstr2) - tempstr2 = e_contact_get_const (contact, E_CONTACT_FULL_NAME); - if (!tempstr2 || !*tempstr2) - tempstr2 = e_contact_get_const (contact, E_CONTACT_ORG); - if (!tempstr2 || !*tempstr2) { - g_free (tempfree); - tempstr2 = get_email (contact, E_CONTACT_EMAIL_1, &tempfree); - } - if (!tempstr2 || !*tempstr2) { - g_free (tempfree); - tempstr2 = get_email (contact, E_CONTACT_EMAIL_2, &tempfree); - } - if (!tempstr2 || !*tempstr2) { - g_free (tempfree); - tempstr2 = get_email (contact, E_CONTACT_EMAIL_3, &tempfree); - } - - if (!tempstr2 || !*tempstr2) - tempstr = g_strdup_printf (_("Contact information")); - else - tempstr = g_strdup_printf (_("Contact information for %s"), tempstr2); - - e_composer_header_table_set_subject (table, tempstr); - - g_free (tempstr); - g_free (tempfree); - } + ccd = g_new0 (CreateComposerData, 1); + ccd->attachment_destinations = g_slist_copy (destinations); + g_slist_foreach (ccd->attachment_destinations, (GFunc) g_object_ref, NULL); - gtk_widget_show (GTK_WIDGET (composer)); + e_msg_composer_new (shell, eab_composer_created_cb, ccd); } diff --git a/modules/composer-autosave/e-autosave-utils.c b/modules/composer-autosave/e-autosave-utils.c index 2e83b2a..19a64b1 100644 --- a/modules/composer-autosave/e-autosave-utils.c +++ b/modules/composer-autosave/e-autosave-utils.c @@ -116,6 +116,43 @@ create_snapshot_file (EMsgComposer *composer, return snapshot_file; } +typedef struct _CreateComposerData { + GSimpleAsyncResult *simple; + LoadContext *context; + CamelMimeMessage *message; + GFile *snapshot_file; +} CreateComposerData; + +static void +autosave_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateComposerData *ccd = user_data; + EMsgComposer *composer; + GError *error = NULL; + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_simple_async_result_take_error (ccd->simple, error); + } else { + e_msg_composer_setup_with_message (composer, ccd->message, TRUE, NULL, NULL); + g_object_set_data_full ( + G_OBJECT (composer), + SNAPSHOT_FILE_KEY, g_object_ref (ccd->snapshot_file), + (GDestroyNotify) delete_snapshot_file); + ccd->context->composer = g_object_ref_sink (composer); + } + + g_simple_async_result_complete (ccd->simple); + + g_clear_object (&ccd->simple); + g_clear_object (&ccd->message); + g_clear_object (&ccd->snapshot_file); + g_free (ccd); +} + static void load_snapshot_loaded_cb (GFile *snapshot_file, GAsyncResult *result, @@ -124,11 +161,11 @@ load_snapshot_loaded_cb (GFile *snapshot_file, EShell *shell; GObject *object; LoadContext *context; - EMsgComposer *composer; CamelMimeMessage *message; CamelStream *camel_stream; gchar *contents = NULL; gsize length; + CreateComposerData *ccd; GError *local_error = NULL; context = g_simple_async_result_get_op_res_gpointer (simple); @@ -140,6 +177,7 @@ load_snapshot_loaded_cb (GFile *snapshot_file, g_warn_if_fail (contents == NULL); g_simple_async_result_take_error (simple, local_error); g_simple_async_result_complete (simple); + g_object_unref (simple); return; } @@ -157,6 +195,7 @@ load_snapshot_loaded_cb (GFile *snapshot_file, g_simple_async_result_take_error (simple, local_error); g_simple_async_result_complete (simple); g_object_unref (message); + g_object_unref (simple); return; } @@ -167,19 +206,16 @@ load_snapshot_loaded_cb (GFile *snapshot_file, * restore its snapshot file so it continues auto-saving to * the same file. */ shell = E_SHELL (object); - g_object_ref (snapshot_file); - composer = e_msg_composer_new_with_message (shell, message, TRUE, NULL, NULL); - g_object_set_data_full ( - G_OBJECT (composer), - SNAPSHOT_FILE_KEY, snapshot_file, - (GDestroyNotify) delete_snapshot_file); - context->composer = g_object_ref_sink (composer); - g_object_unref (message); - g_object_unref (object); + ccd = g_new0 (CreateComposerData, 1); + ccd->simple = simple; + ccd->context = context; + ccd->message = message; + ccd->snapshot_file = g_object_ref (snapshot_file); - g_simple_async_result_complete (simple); - g_object_unref (simple); + e_msg_composer_new (shell, autosave_composer_created_cb, ccd); + + g_object_unref (object); } static void diff --git a/modules/composer-autosave/e-composer-autosave.c b/modules/composer-autosave/e-composer-autosave.c index c794593..eabc2e1 100644 --- a/modules/composer-autosave/e-composer-autosave.c +++ b/modules/composer-autosave/e-composer-autosave.c @@ -120,15 +120,15 @@ static void composer_autosave_changed_cb (EComposerAutosave *autosave) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; EExtensible *extensible; extensible = e_extension_get_extensible (E_EXTENSION (autosave)); editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible)); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); - if (autosave->priv->timeout_id == 0 && e_html_editor_view_get_changed (view)) { + if (autosave->priv->timeout_id == 0 && e_content_editor_get_changed (cnt_editor)) { autosave->priv->timeout_id = e_named_timeout_add_seconds ( AUTOSAVE_INTERVAL, composer_autosave_timeout_cb, autosave); @@ -160,7 +160,7 @@ static void composer_autosave_constructed (GObject *object) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; EExtensible *extensible; /* Chain up to parent's constructed() method. */ @@ -168,12 +168,12 @@ composer_autosave_constructed (GObject *object) extensible = e_extension_get_extensible (E_EXTENSION (object)); editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible)); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); /* Do not use e_signal_connect_notify_swapped() here, this module relies on "false" change notifications. */ g_signal_connect_swapped ( - view, "notify::changed", + cnt_editor, "notify::changed", G_CALLBACK (composer_autosave_changed_cb), object); } diff --git a/modules/itip-formatter/Makefile.am b/modules/itip-formatter/Makefile.am index 9252ad8..f8a6241 100644 --- a/modules/itip-formatter/Makefile.am +++ b/modules/itip-formatter/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS = web-extension + @EVO_PLUGIN_RULE@ module_LTLIBRARIES = module-itip-formatter.la @@ -21,7 +23,8 @@ module_itip_formatter_la_SOURCES = \ e-mail-part-itip.h \ itip-view.c \ itip-view.h \ - evolution-module-itip-formatter.c + evolution-module-itip-formatter.c \ + itip-view-elements-defines.h module_itip_formatter_la_LIBADD = \ $(top_builddir)/e-util/libevolution-util.la \ diff --git a/modules/itip-formatter/e-mail-formatter-itip.c b/modules/itip-formatter/e-mail-formatter-itip.c index 1439cf1..9ac5751 100644 --- a/modules/itip-formatter/e-mail-formatter-itip.c +++ b/modules/itip-formatter/e-mail-formatter-itip.c @@ -66,18 +66,25 @@ emfe_itip_format (EMailFormatterExtension *extension, itip_part = (EMailPartItip *) part; if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) { - buffer = g_string_sized_new (1024); + ItipView *itip_view; - itip_part->view = itip_view_new ( - itip_part, itip_part->client_cache); + buffer = g_string_sized_new (1024); - itip_view_init_view (itip_part->view); - itip_view_write_for_printing (itip_part->view, buffer); + itip_view = itip_view_new (0, e_mail_part_get_id (part), + itip_part, + itip_part->folder, + itip_part->message_uid, + itip_part->message, + itip_part->itip_mime_part, + itip_part->vcalendar, + itip_part->cancellable); + itip_view_init_view (itip_view); + itip_view_write_for_printing (itip_view, buffer); } else if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { buffer = g_string_sized_new (2048); - itip_view_write (formatter, buffer); + itip_view_write (itip_part, formatter, buffer); } else { CamelFolder *folder; @@ -101,8 +108,8 @@ emfe_itip_format (EMailFormatterExtension *extension, } itip_part->folder = g_object_ref (folder); - itip_part->uid = g_strdup (message_uid); - itip_part->msg = g_object_ref (message); + itip_part->message_uid = g_strdup (message_uid); + itip_part->message = g_object_ref (message); default_charset = e_mail_formatter_get_default_charset (formatter); charset = e_mail_formatter_get_charset (formatter); diff --git a/modules/itip-formatter/e-mail-parser-itip.c b/modules/itip-formatter/e-mail-parser-itip.c index bd2e961..6b60153 100644 --- a/modules/itip-formatter/e-mail-parser-itip.c +++ b/modules/itip-formatter/e-mail-parser-itip.c @@ -38,8 +38,6 @@ #include "e-mail-part-itip.h" #include "itip-view.h" -#define CONF_KEY_DELETE "delete-processed" - #define d(x) typedef EMailParserExtension EMailParserItip; @@ -69,9 +67,6 @@ empe_itip_parse (EMailParserExtension *extension, GCancellable *cancellable, GQueue *out_mail_parts) { - EShell *shell; - GSettings *settings; - EClientCache *client_cache; EMailPartItip *itip_part; CamelDataWrapper *content; CamelStream *stream; @@ -83,21 +78,8 @@ empe_itip_parse (EMailParserExtension *extension, len = part_id->len; g_string_append_printf (part_id, ".itip"); - settings = e_util_ref_settings ("org.gnome.evolution.plugin.itip"); - - shell = e_shell_get_default (); - client_cache = e_shell_get_client_cache (shell); - itip_part = e_mail_part_itip_new (part, part_id->str); - itip_part->delete_message = g_settings_get_boolean (settings, CONF_KEY_DELETE); - itip_part->has_organizer = FALSE; - itip_part->no_reply_wanted = FALSE; - itip_part->part = part; - itip_part->cancellable = g_cancellable_new (); - itip_part->real_comps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - itip_part->client_cache = g_object_ref (client_cache); - - g_object_unref (settings); + itip_part->itip_mime_part = g_object_ref (part); /* This is non-gui thread. Download the part for using in the main thread */ content = camel_medium_get_content ((CamelMedium *) part); @@ -153,4 +135,3 @@ e_mail_parser_itip_type_register (GTypeModule *type_module) { e_mail_parser_itip_register_type (type_module); } - diff --git a/modules/itip-formatter/e-mail-part-itip.c b/modules/itip-formatter/e-mail-part-itip.c index 8a84ae7..fbb4ffb 100644 --- a/modules/itip-formatter/e-mail-part-itip.c +++ b/modules/itip-formatter/e-mail-part-itip.c @@ -15,6 +15,13 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <e-util/e-util.h> + #include "e-mail-part-itip.h" #define E_MAIL_PART_ITIP_GET_PRIVATE(obj) \ @@ -22,7 +29,7 @@ ((obj), E_TYPE_MAIL_PART_ITIP, EMailPartItipPrivate)) struct _EMailPartItipPrivate { - gint placeholder; + GSList *views; /* ItipView * */ }; G_DEFINE_DYNAMIC_TYPE ( @@ -37,10 +44,16 @@ mail_part_itip_dispose (GObject *object) g_cancellable_cancel (part->cancellable); + g_free (part->message_uid); + part->message_uid = NULL; + + g_free (part->vcalendar); + part->vcalendar = NULL; + + g_clear_object (&part->folder); + g_clear_object (&part->message); + g_clear_object (&part->itip_mime_part); g_clear_object (&part->cancellable); - g_clear_object (&part->client_cache); - g_clear_object (&part->comp); - g_clear_object (&part->view); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_mail_part_itip_parent_class)->dispose (object); @@ -51,66 +64,48 @@ mail_part_itip_finalize (GObject *object) { EMailPartItip *part = E_MAIL_PART_ITIP (object); - g_free (part->vcalendar); - g_free (part->calendar_uid); - g_free (part->from_address); - g_free (part->from_name); - g_free (part->to_address); - g_free (part->to_name); - g_free (part->delegator_address); - g_free (part->delegator_name); - g_free (part->my_address); - g_free (part->uid); - - if (part->top_level != NULL) - icalcomponent_free (part->top_level); - - if (part->main_comp != NULL) - icalcomponent_free (part->main_comp); - - g_hash_table_destroy (part->real_comps); + g_slist_free_full (part->priv->views, g_object_unref); + part->priv->views = NULL; /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_mail_part_itip_parent_class)->finalize (object); } static void -mail_part_itip_bind_dom_element (EMailPart *part, - WebKitDOMElement *element) +mail_part_itip_web_view_loaded (EMailPart *mail_part, + EWebView *web_view) { - GString *buffer; - WebKitDOMDocument *document; - WebKitDOMElement *bind_element, *document_element; - ItipView *view; EMailPartItip *pitip; - - pitip = E_MAIL_PART_ITIP (part); - - bind_element = element; - if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (bind_element)) - element = webkit_dom_element_query_selector (element, "iframe", NULL); - - g_return_if_fail (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)); - - /* A view is already assigned for this element. */ - if (g_object_get_data (G_OBJECT (element), "view")) - return; - - buffer = g_string_new (""); - document = webkit_dom_html_iframe_element_get_content_document ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (element)); - - view = itip_view_new (pitip, pitip->client_cache); - g_object_set_data_full ( - G_OBJECT (element), "view", view, - (GDestroyNotify) g_object_unref); - - document_element = webkit_dom_document_get_document_element (document); - itip_view_create_dom_bindings (view, document_element); - g_object_unref (document_element); - - itip_view_init_view (view); - g_string_free (buffer, TRUE); + ItipView *itip_view; + + g_return_if_fail (E_IS_MAIL_PART_ITIP (mail_part)); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + pitip = E_MAIL_PART_ITIP (mail_part); + + /* FIXME WK2 - it can sometimes happen that the pitip members, like the folder, message_uid and message, + are not initialized yet, because the internal frame in the main EWebView is not passed + through the EMailFormatter, where these are set. This requires a new signal on the WebKitWebView, + ideally, to call this only after the iframe is truly loaded (these pitip members are only a side + effect of a whole issue with non-knowing that a particular iframe was fully loaded). + + Also retest what happens when the same meeting is opened in multiple windows; it could crash in gtk+ + when a button was clicked in one or the other, but also not always. + */ + itip_view = itip_view_new ( + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)), + e_mail_part_get_id (mail_part), + pitip, + pitip->folder, + pitip->message_uid, + pitip->message, + pitip->itip_mime_part, + pitip->vcalendar, + pitip->cancellable); + + itip_view_set_web_view (itip_view, web_view); + + pitip->priv->views = g_slist_prepend (pitip->priv->views, itip_view); } static void @@ -126,7 +121,7 @@ e_mail_part_itip_class_init (EMailPartItipClass *class) object_class->finalize = mail_part_itip_finalize; mail_part_class = E_MAIL_PART_CLASS (class); - mail_part_class->bind_dom_element = mail_part_itip_bind_dom_element; + mail_part_class->web_view_loaded = mail_part_itip_web_view_loaded; } static void @@ -138,6 +133,7 @@ static void e_mail_part_itip_init (EMailPartItip *part) { part->priv = E_MAIL_PART_ITIP_GET_PRIVATE (part); + part->cancellable = g_cancellable_new (); e_mail_part_set_mime_type (E_MAIL_PART (part), "text/calendar"); @@ -163,4 +159,3 @@ e_mail_part_itip_new (CamelMimePart *mime_part, E_TYPE_MAIL_PART_ITIP, "id", id, "mime-part", mime_part, NULL); } - diff --git a/modules/itip-formatter/e-mail-part-itip.h b/modules/itip-formatter/e-mail-part-itip.h index 42c1828..49f5374 100644 --- a/modules/itip-formatter/e-mail-part-itip.h +++ b/modules/itip-formatter/e-mail-part-itip.h @@ -55,79 +55,13 @@ struct _EMailPartItip { EMailPartItipPrivate *priv; CamelFolder *folder; - CamelMimeMessage *msg; - CamelMimePart *part; - - gchar *uid; - - EClientCache *client_cache; - - ECalClient *current_client; - ECalClientSourceType type; + CamelMimeMessage *message; + gchar *message_uid; + CamelMimePart *itip_mime_part; + gchar *vcalendar; /* cancelled when freeing the puri */ GCancellable *cancellable; - - gchar *vcalendar; - ECalComponent *comp; - icalcomponent *main_comp; - icalcomponent *ical_comp; - icalcomponent *top_level; - icalcompiter iter; - icalproperty_method method; - time_t start_time; - time_t end_time; - - gint current; - gint total; - - gchar *calendar_uid; - - gchar *from_address; - gchar *from_name; - gchar *to_address; - gchar *to_name; - gchar *delegator_address; - gchar *delegator_name; - gchar *my_address; - gint view_only; - - guint progress_info_id; - - gboolean delete_message; - /* a reply can only be sent if and only if there is an organizer */ - gboolean has_organizer; - /* - * Usually replies are sent unless the user unchecks that option. - * There are some cases when the default is not to sent a reply - * (but the user can still chose to do so by checking the option): - * - the organizer explicitly set RSVP=FALSE for the current user - * - the event has no ATTENDEEs: that's the case for most non-meeting - * events - * - * The last case is meant for forwarded non-meeting - * events. Traditionally Evolution hasn't offered to send a - * reply, therefore the updated implementation mimics that - * behavior. - * - * Unfortunately some software apparently strips all ATTENDEEs - * when forwarding a meeting; in that case sending a reply is - * also unchecked by default. So the check for ATTENDEEs is a - * tradeoff between sending unwanted replies in cases where - * that wasn't done in the past and not sending a possibly - * wanted reply where that wasn't possible in the past - * (because replies to forwarded events were not - * supported). Overall that should be an improvement, and the - * user can always override the default. - */ - gboolean no_reply_wanted; - - guint update_item_progress_info_id; - guint update_item_error_info_id; - ItipViewResponse update_item_response; - GHashTable *real_comps; /* ESource's UID -> ECalComponent stored on the server */ - - ItipView *view; }; struct _EMailPartItipClass { diff --git a/modules/itip-formatter/itip-view-elements-defines.h b/modules/itip-formatter/itip-view-elements-defines.h new file mode 100644 index 0000000..35bf653 --- /dev/null +++ b/modules/itip-formatter/itip-view-elements-defines.h @@ -0,0 +1,65 @@ +/* + * itip-view-elements-defines.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef ITIP_VIEW_ELEMENTS_DEFINES_H +#define ITIP_VIEW_ELEMENTS_DEFINES_H + +#define TEXT_ROW_SENDER "text_row_sender" +#define TABLE_ROW_SUMMARY "table_row_summary" +#define TABLE_ROW_LOCATION "table_row_location" +#define TABLE_ROW_START_DATE "table_row_start_time" +#define TABLE_ROW_END_DATE "table_row_end_time" +#define TABLE_ROW_STATUS "table_row_status" +#define TABLE_ROW_COMMENT "table_row_comment" +#define TABLE_ROW_DESCRIPTION "table_row_description" +#define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment" +#define TABLE_ROW_ESCB "table_row_escb" +#define TABLE_ROW_BUTTONS "table_row_buttons" +#define TABLE_ROW_ESCB_LABEL "table_row_escb_label" + +#define TABLE_BUTTONS "table_buttons" + +#define SELECT_ESOURCE "select_esource" +#define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment" + +#define CHECKBOX_RSVP "checkbox_rsvp" +#define CHECKBOX_RECUR "checkbox_recur" +#define CHECKBOX_UPDATE "checkbox_update" +#define CHECKBOX_FREE_TIME "checkbox_free_time" +#define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm" +#define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm" + +#define BUTTON_OPEN_CALENDAR "button_open_calendar" +#define BUTTON_DECLINE "button_decline" +#define BUTTON_DECLINE_ALL "button_decline_all" +#define BUTTON_ACCEPT "button_accept" +#define BUTTON_ACCEPT_ALL "button_accept_all" +#define BUTTON_TENTATIVE "button_tentative" +#define BUTTON_TENTATIVE_ALL "button_tentative_all" +#define BUTTON_SEND_INFORMATION "button_send_information" +#define BUTTON_UPDATE "button_update" +#define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status" +#define BUTTON_SAVE "button_save" + +#define TABLE_UPPER_ITIP_INFO "table_upper_itip_info" +#define TABLE_LOWER_ITIP_INFO "table_lower_itip_info" + +#define DIV_ITIP_CONTENT "div_itip_content" +#define DIV_ITIP_ERROR "div_itip_error" + +#endif /* ITIP_VIEW_ELEMENTS_DEFINES_H */ diff --git a/modules/itip-formatter/itip-view.c b/modules/itip-formatter/itip-view.c index 42b7669..482d2c1 100644 --- a/modules/itip-formatter/itip-view.c +++ b/modules/itip-formatter/itip-view.c @@ -25,7 +25,6 @@ #include <string.h> #include <glib/gi18n.h> -#include <webkit/webkitdom.h> #include <libedataserver/libedataserver.h> #include <shell/e-shell.h> @@ -40,6 +39,10 @@ #include "itip-view.h" #include "e-mail-part-itip.h" +#include "itip-view-elements-defines.h" + +#include "web-extension/module-itip-formatter-web-extension.h" + #define d(x) #define MEETING_ICON "stock_people" @@ -105,54 +108,85 @@ struct _ItipViewPrivate { gint needs_decline : 1; - WebKitDOMDocument *dom_document; - EMailPartItip *itip_part; + gpointer itip_part_ptr; /* not referenced, only for a "reference" to which part this belongs */ + + GDBusProxy *web_extension; + guint web_extension_watch_name_id; + guint web_extension_source_changed_cb_signal_id; + guint web_extension_recur_toggled_signal_id; + + guint64 page_id; + gchar *part_id; gchar *error; -}; + GWeakRef *web_view_weakref; + + CamelFolder *folder; + CamelMimeMessage *message; + gchar *message_uid; + CamelMimePart *itip_mime_part; + GCancellable *cancellable; -#define TEXT_ROW_SENDER "text_row_sender" -#define TABLE_ROW_SUMMARY "table_row_summary" -#define TABLE_ROW_LOCATION "table_row_location" -#define TABLE_ROW_START_DATE "table_row_start_time" -#define TABLE_ROW_END_DATE "table_row_end_time" -#define TABLE_ROW_STATUS "table_row_status" -#define TABLE_ROW_COMMENT "table_row_comment" -#define TABLE_ROW_DESCRIPTION "table_row_description" -#define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment" -#define TABLE_ROW_ESCB "table_row_escb" -#define TABLE_ROW_BUTTONS "table_row_buttons" -#define TABLE_ROW_ESCB_LABEL "table_row_escb_label" - -#define TABLE_BUTTONS "table_buttons" - -#define SELECT_ESOURCE "select_esource" -#define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment" - -#define CHECKBOX_RSVP "checkbox_rsvp" -#define CHECKBOX_RECUR "checkbox_recur" -#define CHECKBOX_UPDATE "checkbox_update" -#define CHECKBOX_FREE_TIME "checkbox_free_time" -#define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm" -#define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm" - -#define BUTTON_OPEN_CALENDAR "button_open_calendar" -#define BUTTON_DECLINE "button_decline" -#define BUTTON_DECLINE_ALL "button_decline_all" -#define BUTTON_ACCEPT "button_accept" -#define BUTTON_ACCEPT_ALL "button_accept_all" -#define BUTTON_TENTATIVE "button_tentative" -#define BUTTON_TENTATIVE_ALL "button_tentative_all" -#define BUTTON_SEND_INFORMATION "button_send_information" -#define BUTTON_UPDATE "button_update" -#define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status" -#define BUTTON_SAVE "button_save" - -#define TABLE_UPPER_ITIP_INFO "table_upper_itip_info" -#define TABLE_LOWER_ITIP_INFO "table_lower_itip_info" - -#define DIV_ITIP_CONTENT "div_itip_content" -#define DIV_ITIP_ERROR "div_itip_error" + ECalClient *current_client; + + gchar *vcalendar; + ECalComponent *comp; + icalcomponent *main_comp; + icalcomponent *ical_comp; + icalcomponent *top_level; + icalcompiter iter; + icalproperty_method method; + time_t start_time; + time_t end_time; + + gint current; + gint total; + + gchar *calendar_uid; + + gchar *from_address; + gchar *from_name; + gchar *to_address; + gchar *to_name; + gchar *delegator_address; + gchar *delegator_name; + gchar *my_address; + gint view_only; + + guint progress_info_id; + + /* a reply can only be sent if and only if there is an organizer */ + gboolean has_organizer; + /* + * Usually replies are sent unless the user unchecks that option. + * There are some cases when the default is not to sent a reply + * (but the user can still chose to do so by checking the option): + * - the organizer explicitly set RSVP=FALSE for the current user + * - the event has no ATTENDEEs: that's the case for most non-meeting + * events + * + * The last case is meant for forwarded non-meeting + * events. Traditionally Evolution hasn't offered to send a + * reply, therefore the updated implementation mimics that + * behavior. + * + * Unfortunately some software apparently strips all ATTENDEEs + * when forwarding a meeting; in that case sending a reply is + * also unchecked by default. So the check for ATTENDEEs is a + * tradeoff between sending unwanted replies in cases where + * that wasn't done in the past and not sending a possibly + * wanted reply where that wasn't possible in the past + * (because replies to forwarded events were not + * supported). Overall that should be an improvement, and the + * user can always override the default. + */ + gboolean no_reply_wanted; + + guint update_item_progress_info_id; + guint update_item_error_info_id; + ItipViewResponse update_item_response; + GHashTable *real_comps; /* ESource's UID -> ECalComponent stored on the server */ +}; enum { PROP_0, @@ -612,6 +646,199 @@ set_journal_sender_text (ItipView *view) } static void +enable_button (ItipView *view, + const gchar *button_id, + gboolean enable) +{ + if (!view->priv->web_extension) + return; + + g_dbus_proxy_call ( + view->priv->web_extension, + "EnableButton", + g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, button_id, enable), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +show_button (ItipView *view, + const gchar *id) +{ + if (!view->priv->web_extension) + return; + + g_dbus_proxy_call ( + view->priv->web_extension, + "ShowButton", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +hide_element (ItipView *view, + const gchar *element_id, + gboolean hide) +{ + if (!view->priv->web_extension) + return; + + g_dbus_proxy_call ( + view->priv->web_extension, + "HideElement", + g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, element_id, hide), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gboolean +element_is_hidden (ItipView *view, + const gchar *element_id) +{ + GVariant *result; + gboolean hidden; + + if (!view->priv->web_extension) + return FALSE; + + result = g_dbus_proxy_call_sync ( + view->priv->web_extension, + "ElementIsHidden", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, element_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(b)", &hidden); + g_variant_unref (result); + return hidden; + } + + return FALSE; +} + +static void +set_inner_html (ItipView *view, + const gchar *element_id, + const gchar *inner_html) +{ + if (!view->priv->web_extension) + return; + + g_dbus_proxy_call ( + view->priv->web_extension, + "ElementSetInnerHTML", + g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, element_id, inner_html), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +input_set_checked (ItipView *view, + const gchar *input_id, + gboolean checked) +{ + if (!view->priv->web_extension) + return; + + g_dbus_proxy_call ( + view->priv->web_extension, + "InputSetChecked", + g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, input_id, checked), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gboolean +input_is_checked (ItipView *view, + const gchar *input_id) +{ + GVariant *result; + gboolean checked; + + if (!view->priv->web_extension) + return FALSE; + + result = g_dbus_proxy_call_sync ( + view->priv->web_extension, + "InputIsChecked", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, input_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(b)", &checked); + g_variant_unref (result); + return checked; + } + + return FALSE; +} + +static void +show_checkbox (ItipView *view, + const gchar *id, + gboolean show, + gboolean update_second) +{ + g_return_if_fail (ITIP_IS_VIEW (view)); + + if (!view->priv->web_extension) + return; + + g_dbus_proxy_call ( + view->priv->web_extension, + "ShowCheckbox", + g_variant_new ("(tssbb)", view->priv->page_id, view->priv->part_id, id, show, update_second), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +set_area_text (ItipView *view, + const gchar *id, + const gchar *text) +{ + g_return_if_fail (ITIP_IS_VIEW (view)); + + if (!view->priv->web_extension) + return; + + g_dbus_proxy_call ( + view->priv->web_extension, + "SetAreaText", + g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, id, text ? text : ""), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void set_sender_text (ItipView *view) { ItipViewPrivate *priv; @@ -635,22 +862,14 @@ set_sender_text (ItipView *view) break; } - if (priv->sender && priv->dom_document) { - WebKitDOMElement *div; - - div = webkit_dom_document_get_element_by_id ( - priv->dom_document, TEXT_ROW_SENDER); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (div), priv->sender, NULL); - g_object_unref (div); - } + if (priv->sender && priv->web_extension) + set_inner_html (view, TEXT_ROW_SENDER, priv->sender); } static void update_start_end_times (ItipView *view) { ItipViewPrivate *priv; - WebKitDOMElement *row, *col; gchar buffer[256]; time_t now; struct tm *now_tm; @@ -695,142 +914,155 @@ update_start_end_times (ItipView *view) } #undef is_same - if (priv->dom_document) { - row = webkit_dom_document_get_element_by_id ( - priv->dom_document, TABLE_ROW_START_DATE); - if (priv->start_header && priv->start_label) { - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), FALSE); - - col = webkit_dom_element_get_first_element_child (row); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (col), priv->start_header, NULL); - g_object_unref (col); - - col = webkit_dom_element_get_last_element_child (row); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (col), priv->start_label, NULL); - g_object_unref (col); - } else { - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), TRUE); - } - g_object_unref (row); - - row = webkit_dom_document_get_element_by_id ( - priv->dom_document, TABLE_ROW_END_DATE); - if (priv->end_header && priv->end_label) { - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), FALSE); - - col = webkit_dom_element_get_first_element_child (row); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (col), priv->end_header, NULL); - g_object_unref (col); - - col = webkit_dom_element_get_last_element_child (row); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (col), priv->end_label, NULL); - g_object_unref (col); - } else { - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), TRUE); - } - g_object_unref (row); - } + if (!priv->web_extension) + return; + + if (priv->start_header && priv->start_label) { + g_dbus_proxy_call ( + priv->web_extension, + "UpdateTimes", + g_variant_new ( + "(tssss)", + view->priv->page_id, + view->priv->part_id, + TABLE_ROW_START_DATE, + priv->start_header, + priv->start_label), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } else + hide_element (view, TABLE_ROW_START_DATE, TRUE); + + if (priv->end_header && priv->end_label) { + g_dbus_proxy_call ( + priv->web_extension, + "UpdateTimes", + g_variant_new ( + "(tssss)", + view->priv->page_id, + view->priv->part_id, + TABLE_ROW_END_DATE, + priv->end_header, + priv->end_label), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } else + hide_element (view, TABLE_ROW_END_DATE, TRUE); } static void -button_clicked_cb (WebKitDOMElement *element, - WebKitDOMEvent *event, - gpointer data) +itip_view_itip_button_clicked_cb (EWebView *web_view, + const gchar *element_class, + const gchar *element_value, + const GtkAllocation *element_position, + gpointer user_data) { - ItipViewResponse response; - gchar *response_str; + ItipView *view = user_data; + gboolean can_use; + gchar *tmp; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (element_class && *element_class); + g_return_if_fail (element_value && *element_value); + g_return_if_fail (ITIP_IS_VIEW (view)); - response_str = webkit_dom_html_button_element_get_value ( - WEBKIT_DOM_HTML_BUTTON_ELEMENT (element)); + tmp = g_strdup_printf ("%p:", view->priv->itip_part_ptr); + can_use = g_str_has_prefix (element_value, tmp); + if (can_use) + element_value += strlen (tmp); + g_free (tmp); - response = atoi (response_str); - g_free (response_str); + if (can_use) { + gint response = atoi (element_value); - g_signal_emit (data, signals[RESPONSE], 0, response); + g_signal_emit (view, signals[RESPONSE], 0, response); + } } static void -rsvp_toggled_cb (WebKitDOMHTMLInputElement *input, - WebKitDOMEvent *event, - gpointer data) +itip_view_register_clicked_listener (ItipView *view) { - WebKitDOMElement *el; + EWebView *web_view; - ItipView *view = data; - gboolean rsvp; + g_return_if_fail (ITIP_IS_VIEW (view)); - rsvp = webkit_dom_html_input_element_get_checked (input); + web_view = itip_view_ref_web_view (view); + if (web_view) { + e_web_view_register_element_clicked (web_view, "itip-button", + itip_view_itip_button_clicked_cb, view); + } - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TEXTAREA_RSVP_COMMENT); - webkit_dom_html_text_area_element_set_disabled ( - WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp); - g_object_unref (el); + g_clear_object (&web_view); } static void -recur_toggled_cb (WebKitDOMHTMLInputElement *input, - WebKitDOMEvent *event, - gpointer data) +recur_toggled_signal_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + ItipView *view) { - ItipView *view = data; + guint64 page_id = 0; + const gchar *part_id = NULL; + + g_return_if_fail (ITIP_IS_VIEW (view)); + + if (g_strcmp0 (signal_name, "RecurToggled") != 0) + return; + + g_variant_get (parameters, "(t&s)", &page_id, &part_id); - itip_view_set_mode (view, view->priv->mode); + if (view->priv->page_id == page_id && + g_strcmp0 (view->priv->part_id, part_id) == 0) + itip_view_set_mode (view, view->priv->mode); } -/* - alarm_check_toggled_cb - check1 was changed, so make the second available based on state of the first check. -*/ static void -alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1, - WebKitDOMEvent *event, - ItipView *view) +source_changed_cb (ItipView *view) { - WebKitDOMElement *check2; - gchar *id; - - id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (check1)); + ESource *source; - if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) { - check2 = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_KEEP_ALARM); - } else { - check2 = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_INHERIT_ALARM); - } + source = itip_view_ref_source (view); - g_free (id); + if (source) { + d (printf ("Source changed to '%s'\n", e_source_get_display_name (source))); + g_signal_emit (view, signals[SOURCE_SELECTED], 0, source); - webkit_dom_html_input_element_set_disabled ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (check2), - (webkit_dom_html_element_get_hidden ( - WEBKIT_DOM_HTML_ELEMENT (check1)) && - webkit_dom_html_input_element_get_checked (check1))); - g_object_unref (check2); + g_object_unref (source); + } } static void -source_changed_cb (WebKitDOMElement *select, - WebKitDOMEvent *event, - ItipView *view) +source_changed_cb_signal_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) { - ESource *source; + ItipView *view = user_data; + guint64 page_id = 0; + const gchar *part_id = NULL; - source = itip_view_ref_source (view); + g_return_if_fail (ITIP_IS_VIEW (view)); + + if (g_strcmp0 (signal_name, "SourceChanged") != 0) + return; - d (printf ("Source changed to '%s'\n", e_source_get_display_name (source))); - g_signal_emit (view, signals[SOURCE_SELECTED], 0, source); + g_variant_get (parameters, "(t&s)", &page_id, &part_id); - g_object_unref (source); + if (view->priv->page_id == page_id && + g_strcmp0 (view->priv->part_id, part_id) == 0) + source_changed_cb (view); } static void @@ -897,19 +1129,8 @@ append_info_item_row (ItipView *view, const gchar *table_id, ItipViewInfoItem *item) { - WebKitDOMElement *table; - WebKitDOMHTMLElement *row, *cell; const gchar *icon_name; - gchar *id; - - table = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, table_id); - row = webkit_dom_html_table_element_insert_row ( - WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL); - - id = g_strdup_printf ("%s_row_%d", table_id, item->id); - webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (row), id); - g_free (id); + gchar *row_id; switch (item->type) { case ITIP_VIEW_INFO_ITEM_TYPE_INFO: @@ -929,42 +1150,31 @@ append_info_item_row (ItipView *view, icon_name = NULL; } - cell = webkit_dom_html_table_row_element_insert_cell ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL); - - if (icon_name) { - gchar *icon_uri; - WebKitDOMElement *image; - WebKitDOMNode *tmp; + row_id = g_strdup_printf ("%s_row_%d", table_id, item->id); - image = webkit_dom_document_create_element ( - view->priv->dom_document, "IMG", NULL); - - icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name); - webkit_dom_html_image_element_set_src ( - WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri); - g_free (icon_uri); - - tmp = webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (cell), - WEBKIT_DOM_NODE (image), - NULL); - - g_object_unref (tmp); - g_object_unref (image); - } + if (!view->priv->web_extension) + return; - g_object_unref (cell); - cell = webkit_dom_html_table_row_element_insert_cell ( - WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL); + g_dbus_proxy_call ( + view->priv->web_extension, + "AppendInfoItemRow", + g_variant_new ( + "(tsssss)", + view->priv->page_id, + view->priv->part_id, + table_id, + row_id, + icon_name, + item->message), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); - webkit_dom_html_element_set_inner_html (cell, item->message, NULL); + g_free (row_id); d (printf ("Added row %s_row_%d ('%s')\n", table_id, item->id, item->message)); - - g_object_unref (table); - g_object_unref (row); - g_object_unref (cell); } static void @@ -972,26 +1182,31 @@ remove_info_item_row (ItipView *view, const gchar *table_id, guint id) { - WebKitDOMElement *row; - WebKitDOMNode *parent, *deleted_node; gchar *row_id; row_id = g_strdup_printf ("%s_row_%d", table_id, id); - row = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, row_id); - g_free (row_id); - parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)), - deleted_node = webkit_dom_node_remove_child ( - parent, WEBKIT_DOM_NODE (row), NULL); - g_object_unref (parent); - g_object_unref (deleted_node); + if (!view->priv->web_extension) + return; + + g_dbus_proxy_call ( + view->priv->web_extension, + "RemoveElement", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, row_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + g_free (row_id); d (printf ("Removed row %s_row_%d\n", table_id, id)); } static void buttons_table_write_button (GString *buffer, + gpointer itip_part_ptr, const gchar *name, const gchar *label, const gchar *icon, @@ -1004,18 +1219,18 @@ buttons_table_write_button (GString *buffer, if (icon) { g_string_append_printf ( buffer, - "<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" hidden disabled>" + "<td><button class=\"itip-button\" type=\"button\" name=\"%s\" value=\"%p:%d\" id=\"%s\" accesskey=\"%s\" hidden disabled>" "<div><img src=\"gtk-stock://%s?size=%d\"> <span>%s</span></div>" "</button></td>\n", - name, response, name, access_key ? access_key : "" , icon, + name, itip_part_ptr, response, name, access_key ? access_key : "" , icon, GTK_ICON_SIZE_BUTTON, html_label); } else { g_string_append_printf ( buffer, - "<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" hidden disabled>" + "<td><button class=\"itip-button\" type=\"button\" name=\"%s\" value=\"%p:%d\" id=\"%s\" accesskey=\"%s\" hidden disabled>" "<div><span>%s</span></div>" "</button></td>\n", - name, response, name, access_key ? access_key : "" , html_label); + name, itip_part_ptr, response, name, access_key ? access_key : "" , html_label); } g_free (html_label); @@ -1025,7 +1240,8 @@ buttons_table_write_button (GString *buffer, } static void -append_buttons_table (GString *buffer) +append_buttons_table (GString *buffer, + gpointer itip_part_ptr) { g_string_append ( buffer, @@ -1036,34 +1252,34 @@ append_buttons_table (GString *buffer) /* Everything gets the open button */ buttons_table_write_button ( - buffer, BUTTON_OPEN_CALENDAR, _("Ope_n Calendar"), + buffer, itip_part_ptr, BUTTON_OPEN_CALENDAR, _("Ope_n Calendar"), "go-jump", ITIP_VIEW_RESPONSE_OPEN); buttons_table_write_button ( - buffer, BUTTON_DECLINE_ALL, _("_Decline all"), + buffer, itip_part_ptr, BUTTON_DECLINE_ALL, _("_Decline all"), NULL, ITIP_VIEW_RESPONSE_DECLINE); buttons_table_write_button ( - buffer, BUTTON_DECLINE, _("_Decline"), + buffer, itip_part_ptr, BUTTON_DECLINE, _("_Decline"), NULL, ITIP_VIEW_RESPONSE_DECLINE); buttons_table_write_button ( - buffer, BUTTON_TENTATIVE_ALL, _("_Tentative all"), + buffer, itip_part_ptr, BUTTON_TENTATIVE_ALL, _("_Tentative all"), NULL, ITIP_VIEW_RESPONSE_TENTATIVE); buttons_table_write_button ( - buffer, BUTTON_TENTATIVE, _("_Tentative"), + buffer, itip_part_ptr, BUTTON_TENTATIVE, _("_Tentative"), NULL, ITIP_VIEW_RESPONSE_TENTATIVE); buttons_table_write_button ( - buffer, BUTTON_ACCEPT_ALL, _("Acce_pt all"), + buffer, itip_part_ptr, BUTTON_ACCEPT_ALL, _("Acce_pt all"), NULL, ITIP_VIEW_RESPONSE_ACCEPT); buttons_table_write_button ( - buffer, BUTTON_ACCEPT, _("Acce_pt"), + buffer, itip_part_ptr, BUTTON_ACCEPT, _("Acce_pt"), NULL, ITIP_VIEW_RESPONSE_ACCEPT); buttons_table_write_button ( - buffer, BUTTON_SEND_INFORMATION, _("Send _Information"), + buffer, itip_part_ptr, BUTTON_SEND_INFORMATION, _("Send _Information"), NULL, ITIP_VIEW_RESPONSE_REFRESH); buttons_table_write_button ( - buffer, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"), + buffer, itip_part_ptr, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"), NULL, ITIP_VIEW_RESPONSE_UPDATE); buttons_table_write_button ( - buffer, BUTTON_UPDATE, _("_Update"), + buffer, itip_part_ptr, BUTTON_UPDATE, _("_Update"), NULL, ITIP_VIEW_RESPONSE_CANCEL); g_string_append (buffer, "</tr></table>"); @@ -1073,14 +1289,12 @@ static void itip_view_rebuild_source_list (ItipView *view) { ESourceRegistry *registry; - WebKitDOMElement *select; GList *list, *link; const gchar *extension_name; - GHashTable *groups; d (printf ("Assigning a new source list!\n")); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return; registry = view->priv->registry; @@ -1089,92 +1303,49 @@ itip_view_rebuild_source_list (ItipView *view) if (extension_name == NULL) return; - select = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, SELECT_ESOURCE); - - while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (select))) { - WebKitDOMNode *removed_child, *last_child; - - last_child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (select)); - removed_child = webkit_dom_node_remove_child ( - WEBKIT_DOM_NODE (select), last_child, NULL); - g_object_unref (last_child); - g_object_unref (removed_child); - } + g_dbus_proxy_call ( + view->priv->web_extension, + "ElementRemoveChildNodes", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); list = e_source_registry_list_enabled (registry, extension_name); - groups = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, g_object_unref); for (link = list; link != NULL; link = g_list_next (link)) { ESource *source = E_SOURCE (link->data); ESource *parent; - WebKitDOMElement *option; - WebKitDOMNode *appended_child; - WebKitDOMHTMLOptGroupElement *optgroup; parent = e_source_registry_ref_source ( registry, e_source_get_parent (source)); - optgroup = g_hash_table_lookup (groups, e_source_get_uid (parent)); - if (!optgroup) { - optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT ( - webkit_dom_document_create_element ( - view->priv->dom_document, - "OPTGROUP", NULL)); - webkit_dom_html_opt_group_element_set_label ( - optgroup, e_source_get_display_name (parent)); - g_hash_table_insert ( - groups, g_strdup (e_source_get_uid (parent)), optgroup); - } - g_object_unref (parent); - - option = webkit_dom_document_create_element ( - view->priv->dom_document, "OPTION", NULL); - webkit_dom_html_option_element_set_value ( - WEBKIT_DOM_HTML_OPTION_ELEMENT (option), - e_source_get_uid (source)); - webkit_dom_html_option_element_set_label ( - WEBKIT_DOM_HTML_OPTION_ELEMENT (option), - e_source_get_display_name (source)); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (option), - e_source_get_display_name (source), NULL); - webkit_dom_element_set_class_name ( - WEBKIT_DOM_ELEMENT (option), "calendar"); - - if (!e_source_get_writable (source)) { - webkit_dom_html_option_element_set_disabled ( - WEBKIT_DOM_HTML_OPTION_ELEMENT (option), TRUE); - } - - appended_child = webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (optgroup), - WEBKIT_DOM_NODE (option), + g_dbus_proxy_call ( + view->priv->web_extension, + "RebuildSourceList", + g_variant_new ( + "(tsssssb)", + view->priv->page_id, + view->priv->part_id, + e_source_get_uid (parent), + e_source_get_display_name (parent), + e_source_get_uid (source), + e_source_get_display_name (source), + e_source_get_writable (source)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, NULL); - g_object_unref (option); - g_object_unref (appended_child); - } - - g_list_free_full (list, (GDestroyNotify) g_object_unref); - list = g_hash_table_get_values (groups); - for (link = list; link != NULL; link = g_list_next (link)) { - WebKitDOMNode *appended_child; - WebKitDOMNode *optgroup = link->data; - - appended_child = webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (select), optgroup, NULL); - g_object_unref (appended_child); + g_object_unref (parent); } - g_list_free (list); - - g_hash_table_destroy (groups); - source_changed_cb (select, NULL, view); + g_list_free_full (list, (GDestroyNotify) g_object_unref); - g_object_unref (select); + source_changed_cb (view); } static void @@ -1292,8 +1463,30 @@ itip_view_dispose (GObject *object) priv->source_removed_handler_id = 0; } + if (priv->web_extension_watch_name_id > 0) { + g_bus_unwatch_name (priv->web_extension_watch_name_id); + priv->web_extension_watch_name_id = 0; + } + + if (priv->web_extension_recur_toggled_signal_id > 0) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection (priv->web_extension), + priv->web_extension_recur_toggled_signal_id); + priv->web_extension_recur_toggled_signal_id = 0; + } + + if (priv->web_extension_source_changed_cb_signal_id > 0) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection (priv->web_extension), + priv->web_extension_source_changed_cb_signal_id); + priv->web_extension_source_changed_cb_signal_id = 0; + } + g_clear_object (&priv->client_cache); g_clear_object (&priv->registry); + g_clear_object (&priv->web_extension); + g_clear_object (&priv->cancellable); + g_clear_object (&priv->comp); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (itip_view_parent_class)->dispose (object); @@ -1309,7 +1502,6 @@ itip_view_finalize (GObject *object) d (printf ("Itip view finalized!\n")); - g_clear_object (&priv->dom_document); g_free (priv->extension_name); g_free (priv->sender); g_free (priv->organizer); @@ -1328,6 +1520,7 @@ itip_view_finalize (GObject *object) g_free (priv->end_label); g_free (priv->description); g_free (priv->error); + g_free (priv->part_id); for (iter = priv->lower_info_items; iter; iter = iter->next) { ItipViewInfoItem *item = iter->data; @@ -1345,6 +1538,31 @@ itip_view_finalize (GObject *object) g_slist_free (priv->upper_info_items); + e_weak_ref_free (priv->web_view_weakref); + + g_free (priv->vcalendar); + g_free (priv->calendar_uid); + g_free (priv->from_address); + g_free (priv->from_name); + g_free (priv->to_address); + g_free (priv->to_name); + g_free (priv->delegator_address); + g_free (priv->delegator_name); + g_free (priv->my_address); + g_free (priv->message_uid); + + g_clear_object (&priv->folder); + g_clear_object (&priv->message); + g_clear_object (&priv->itip_mime_part); + + if (priv->top_level != NULL) + icalcomponent_free (priv->top_level); + + if (priv->main_comp != NULL) + icalcomponent_free (priv->main_comp); + + g_hash_table_destroy (priv->real_comps); + /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (itip_view_parent_class)->finalize (object); } @@ -1403,8 +1621,7 @@ itip_view_class_init (ItipViewClass *class) "Client Cache", "Cache of shared EClient instances", E_TYPE_CLIENT_CACHE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READABLE)); g_object_class_install_property ( object_class, @@ -1437,14 +1654,6 @@ itip_view_class_init (ItipViewClass *class) G_TYPE_INT); } -EMailPartItip * -itip_view_get_mail_part (ItipView *view) -{ - g_return_val_if_fail (ITIP_IS_VIEW (view), NULL); - - return view->priv->itip_part; -} - EClientCache * itip_view_get_client_cache (ItipView *view) { @@ -1480,7 +1689,8 @@ itip_view_set_extension_name (ItipView *view, } void -itip_view_write (EMailFormatter *formatter, +itip_view_write (gpointer itip_part_ptr, + EMailFormatter *formatter, GString *buffer) { gchar *header = e_mail_formatter_get_html_header (formatter); @@ -1577,7 +1787,7 @@ itip_view_write (EMailFormatter *formatter, g_string_append (buffer, "</table>\n"); /* Buttons table */ - append_buttons_table (buffer); + append_buttons_table (buffer, itip_part_ptr); /* <div class="itip content" > */ g_string_append (buffer, "</div>\n"); @@ -1650,185 +1860,173 @@ itip_view_write_for_printing (ItipView *view, } } -void -itip_view_create_dom_bindings (ItipView *view, - WebKitDOMElement *element) +static void +web_extension_proxy_created_cb (GDBusProxy *proxy, + GAsyncResult *result, + ItipView *view) { - WebKitDOMElement *el; - WebKitDOMDocument *doc; - - doc = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); - view->priv->dom_document = g_object_ref (doc); - - el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RECUR); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (recur_toggled_cb), FALSE, view); - } - - el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RSVP); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (rsvp_toggled_cb), FALSE, view); - } - - el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_INHERIT_ALARM); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (alarm_check_toggled_cb), FALSE, view); - } - - el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_KEEP_ALARM); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (alarm_check_toggled_cb), FALSE, view); - } - - el = webkit_dom_document_get_element_by_id (doc, BUTTON_OPEN_CALENDAR); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } + GError *error = NULL; - el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); + view->priv->web_extension = g_dbus_proxy_new_finish (result, &error); + if (!view->priv->web_extension) { + g_warning ("Error creating web extension proxy: %s\n", error->message); + g_error_free (error); } - el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT_ALL); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } + view->priv->web_extension_source_changed_cb_signal_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (view->priv->web_extension), + g_dbus_proxy_get_name (view->priv->web_extension), + MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE, + "SourceChanged", + MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + source_changed_cb_signal_cb, + view, + NULL); - el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } + view->priv->web_extension_recur_toggled_signal_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (view->priv->web_extension), + g_dbus_proxy_get_name (view->priv->web_extension), + MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE, + "RecurToggled", + MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback) recur_toggled_signal_cb, + view, + NULL); - el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE_ALL); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } + g_dbus_proxy_call ( + view->priv->web_extension, + "CreateDOMBindings", + g_variant_new ("(ts)", view->priv->page_id, view->priv->part_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); - el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } + itip_view_init_view (view); +} - el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE_ALL); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } +static void +web_extension_appeared_cb (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + ItipView *view) +{ + g_dbus_proxy_new ( + connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + name, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE, + NULL, + (GAsyncReadyCallback)web_extension_proxy_created_cb, + view); +} - el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } +static void +web_extension_vanished_cb (GDBusConnection *connection, + const gchar *name, + ItipView *view) +{ + g_clear_object (&view->priv->web_extension); +} - el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE_ATTENDEE_STATUS); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } +static void +itip_view_watch_web_extension (ItipView *view) +{ + view->priv->web_extension_watch_name_id = + g_bus_watch_name ( + G_BUS_TYPE_SESSION, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_SERVICE_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + (GBusNameAppearedCallback) web_extension_appeared_cb, + (GBusNameVanishedCallback) web_extension_vanished_cb, + view, NULL); +} - el = webkit_dom_document_get_element_by_id (doc, BUTTON_SEND_INFORMATION); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); - } +GDBusProxy * +itip_view_get_web_extension_proxy (ItipView *view) +{ + g_return_val_if_fail (ITIP_IS_VIEW (view), NULL); - el = webkit_dom_document_get_element_by_id (doc, SELECT_ESOURCE); - if (el) { - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "change", - G_CALLBACK (source_changed_cb), FALSE, view); - } + return view->priv->web_extension; } static void itip_view_init (ItipView *view) { + EShell *shell; + EClientCache *client_cache; + + shell = e_shell_get_default (); + client_cache = e_shell_get_client_cache (shell); + view->priv = ITIP_VIEW_GET_PRIVATE (view); + view->priv->web_view_weakref = e_weak_ref_new (NULL); + view->priv->real_comps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + view->priv->client_cache = g_object_ref (client_cache); } ItipView * -itip_view_new (EMailPartItip *puri, - EClientCache *client_cache) +itip_view_new (guint64 page_id, + const gchar *part_id, + gpointer itip_part_ptr, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + CamelMimePart *itip_mime_part, + const gchar *vcalendar, + GCancellable *cancellable) { ItipView *view; - g_return_val_if_fail (E_IS_CLIENT_CACHE (client_cache), NULL); + view = ITIP_VIEW (g_object_new (ITIP_TYPE_VIEW, NULL)); + view->priv->page_id = page_id; + view->priv->part_id = g_strdup (part_id); + view->priv->itip_part_ptr = itip_part_ptr; + view->priv->folder = g_object_ref (folder); + view->priv->message_uid = g_strdup (message_uid); + view->priv->message = g_object_ref (message); + view->priv->itip_mime_part = g_object_ref (itip_mime_part); + view->priv->vcalendar = g_strdup (vcalendar); + view->priv->cancellable = g_object_ref (cancellable); - view = ITIP_VIEW (g_object_new ( - ITIP_TYPE_VIEW, - "client-cache", client_cache, - NULL)); - view->priv->itip_part = puri; + itip_view_watch_web_extension (view); return view; } -static void -show_button (ItipView *view, - const gchar *id) -{ - WebKitDOMElement *button; - - button = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, id); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (button), FALSE); - g_object_unref (button); -} - void itip_view_set_mode (ItipView *view, ItipViewMode mode) { - WebKitDOMElement *row, *cell; - WebKitDOMElement *button; - g_return_if_fail (ITIP_IS_VIEW (view)); view->priv->mode = mode; set_sender_text (view); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return; - row = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_BUTTONS); - cell = webkit_dom_element_get_first_element_child (row); - do { - button = webkit_dom_element_get_first_element_child (cell); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (button), TRUE); - g_object_unref (button); - } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL); - - g_object_unref (row); + g_dbus_proxy_call ( + view->priv->web_extension, + "ElementHideChildNodes", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, TABLE_ROW_BUTTONS), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); view->priv->is_recur_set = itip_view_get_recur_check_state (view); @@ -1886,7 +2084,6 @@ void itip_view_set_item_type (ItipView *view, ECalClientSourceType type) { - WebKitDOMElement *label; const gchar *header; gchar *access_key, *html_label; @@ -1894,12 +2091,9 @@ itip_view_set_item_type (ItipView *view, view->priv->type = type; - if (!view->priv->dom_document) + if (!view->priv->web_extension) return; - label = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_ESCB_LABEL); - switch (view->priv->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: header = _("_Calendar:"); @@ -1922,12 +2116,18 @@ itip_view_set_item_type (ItipView *view, html_label = e_mail_formatter_parse_html_mnemonics (header, &access_key); - webkit_dom_html_element_set_access_key ( - WEBKIT_DOM_HTML_ELEMENT (label), access_key); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (label), html_label, NULL); + g_dbus_proxy_call ( + view->priv->web_extension, + "ElementSetAccessKey", + g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, TABLE_ROW_ESCB_LABEL, access_key), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + set_inner_html (view, TABLE_ROW_ESCB_LABEL, html_label); - g_object_unref (label); g_free (html_label); if (access_key) @@ -2080,8 +2280,6 @@ void itip_view_set_summary (ItipView *view, const gchar *summary) { - WebKitDOMElement *row, *col; - g_return_if_fail (ITIP_IS_VIEW (view)); if (view->priv->summary) @@ -2089,21 +2287,7 @@ itip_view_set_summary (ItipView *view, view->priv->summary = summary ? g_strstrip (e_utf8_ensure_valid (summary)) : NULL; - if (!view->priv->dom_document) - return; - - row = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_SUMMARY); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->summary == NULL)); - - col = webkit_dom_element_get_last_element_child (row); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (col), - view->priv->summary ? view->priv->summary : "", - NULL); - g_object_unref (row); - g_object_unref (col); + set_area_text (view, TABLE_ROW_SUMMARY, view->priv->summary); } const gchar * @@ -2118,8 +2302,6 @@ void itip_view_set_location (ItipView *view, const gchar *location) { - WebKitDOMElement *row, *col; - g_return_if_fail (ITIP_IS_VIEW (view)); if (view->priv->location) @@ -2127,21 +2309,7 @@ itip_view_set_location (ItipView *view, view->priv->location = location ? g_strstrip (e_utf8_ensure_valid (location)) : NULL; - if (!view->priv->dom_document) - return; - - row = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_LOCATION); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->location == NULL)); - - col = webkit_dom_element_get_last_element_child (row); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (col), - view->priv->location ? view->priv->location : "", - NULL); - g_object_unref (row); - g_object_unref (col); + set_area_text (view, TABLE_ROW_LOCATION, view->priv->location); } const gchar * @@ -2156,8 +2324,6 @@ void itip_view_set_status (ItipView *view, const gchar *status) { - WebKitDOMElement *row, *col; - g_return_if_fail (ITIP_IS_VIEW (view)); if (view->priv->status) @@ -2165,21 +2331,7 @@ itip_view_set_status (ItipView *view, view->priv->status = status ? g_strstrip (e_utf8_ensure_valid (status)) : NULL; - if (!view->priv->dom_document) - return; - - row = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_STATUS); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->status == NULL)); - - col = webkit_dom_element_get_last_element_child (row); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (col), - view->priv->status ? view->priv->status : "", - NULL); - g_object_unref (row); - g_object_unref (col); + set_area_text (view, TABLE_ROW_STATUS, view->priv->status); } const gchar * @@ -2194,8 +2346,6 @@ void itip_view_set_comment (ItipView *view, const gchar *comment) { - WebKitDOMElement *row, *col; - g_return_if_fail (ITIP_IS_VIEW (view)); if (view->priv->comment) @@ -2203,21 +2353,7 @@ itip_view_set_comment (ItipView *view, view->priv->comment = comment ? g_strstrip (e_utf8_ensure_valid (comment)) : NULL; - if (!view->priv->dom_document) - return; - - row = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_COMMENT); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->comment == NULL)); - - col = webkit_dom_element_get_last_element_child (row); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (col), - view->priv->comment ? view->priv->comment : "", - NULL); - g_object_unref (row); - g_object_unref (col); + set_area_text (view, TABLE_ROW_COMMENT, view->priv->comment); } const gchar * @@ -2232,8 +2368,6 @@ void itip_view_set_description (ItipView *view, const gchar *description) { - WebKitDOMElement *div; - g_return_if_fail (ITIP_IS_VIEW (view)); if (view->priv->description) @@ -2241,19 +2375,11 @@ itip_view_set_description (ItipView *view, view->priv->description = description ? g_strstrip (e_utf8_ensure_valid (description)) : NULL; - if (!view->priv->dom_document) - return; - - div = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_DESCRIPTION); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (div), (view->priv->description == NULL)); - - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (div), - view->priv->description ? view->priv->description : "", - NULL); - g_object_unref (div); + hide_element (view, TABLE_ROW_DESCRIPTION, (view->priv->description == NULL)); + set_inner_html ( + view, + TABLE_ROW_DESCRIPTION, + view->priv->description ? view->priv->description : ""); } const gchar * @@ -2360,7 +2486,7 @@ itip_view_add_upper_info_item (ItipView *view, priv->upper_info_items = g_slist_append (priv->upper_info_items, item); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return item->id; append_info_item_row (view, TABLE_UPPER_ITIP_INFO, item); @@ -2410,8 +2536,7 @@ itip_view_remove_upper_info_item (ItipView *view, g_free (item->message); g_free (item); - if (!view->priv->dom_document) - remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id); + remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id); return; } @@ -2431,8 +2556,7 @@ itip_view_clear_upper_info_items (ItipView *view) for (l = priv->upper_info_items; l; l = l->next) { ItipViewInfoItem *item = l->data; - if (view->priv->dom_document) - remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id); + remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id); g_free (item->message); g_free (item); @@ -2462,7 +2586,7 @@ itip_view_add_lower_info_item (ItipView *view, priv->lower_info_items = g_slist_append (priv->lower_info_items, item); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return item->id; append_info_item_row (view, TABLE_LOWER_ITIP_INFO, item); @@ -2512,8 +2636,7 @@ itip_view_remove_lower_info_item (ItipView *view, g_free (item->message); g_free (item); - if (view->priv->dom_document) - remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id); + remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id); return; } @@ -2533,8 +2656,7 @@ itip_view_clear_lower_info_items (ItipView *view) for (l = priv->lower_info_items; l; l = l->next) { ItipViewInfoItem *item = l->data; - if (view->priv->dom_document) - remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id); + remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id); g_free (item->message); g_free (item); @@ -2548,112 +2670,124 @@ void itip_view_set_source (ItipView *view, ESource *source) { - WebKitDOMElement *select; - WebKitDOMElement *row; ESource *selected_source; - gulong i, len; g_return_if_fail (ITIP_IS_VIEW (view)); d (printf ("Settings default source '%s'\n", e_source_get_display_name (source))); - if (!view->priv->dom_document) - return; + hide_element (view, TABLE_ROW_ESCB, (source == NULL)); - row = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_ESCB); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (row), (source == NULL)); - g_object_unref (row); - if (source == NULL) + if (!source) return; - select = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, SELECT_ESOURCE); - /* <select> does not emit 'change' event when already selected * <option> is re-selected, but we need to notify itip formatter, * so that it would make all the buttons sensitive */ selected_source = itip_view_ref_source (view); if (source == selected_source) { - source_changed_cb (select, NULL, view); + source_changed_cb (view); return; } if (selected_source != NULL) g_object_unref (selected_source); - if (webkit_dom_html_select_element_get_disabled ( - WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) { - webkit_dom_html_select_element_set_disabled ( - WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE); - } - - len = webkit_dom_html_select_element_get_length ( - WEBKIT_DOM_HTML_SELECT_ELEMENT (select)); - for (i = 0; i < len; i++) { - - WebKitDOMNode *node; - WebKitDOMHTMLOptionElement *option; - gchar *value; - - node = webkit_dom_html_select_element_item ( - WEBKIT_DOM_HTML_SELECT_ELEMENT (select), i); - option = WEBKIT_DOM_HTML_OPTION_ELEMENT (node); - - value = webkit_dom_html_option_element_get_value (option); - if (g_strcmp0 (value, e_source_get_uid (source)) == 0) { - webkit_dom_html_option_element_set_selected ( - option, TRUE); - - g_free (value); - break; - } + if (!view->priv->web_extension) + return; - g_object_unref (node); - g_free (value); - } + g_dbus_proxy_call ( + view->priv->web_extension, + "EnableSelect", + g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); - source_changed_cb (select, NULL, view); + g_dbus_proxy_call ( + view->priv->web_extension, + "SelectSetSelected", + g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE, e_source_get_uid (source)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); - g_object_unref (select); + source_changed_cb (view); } ESource * itip_view_ref_source (ItipView *view) { - WebKitDOMElement *select; - gchar *uid; - ESource *source; - gboolean disable = FALSE; + ESource *source = NULL; + gboolean disable = FALSE, enabled = FALSE; + GVariant *result; g_return_val_if_fail (ITIP_IS_VIEW (view), NULL); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return NULL; - select = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, SELECT_ESOURCE); - if (webkit_dom_html_select_element_get_disabled ( - WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) { - webkit_dom_html_select_element_set_disabled ( - WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE); + result = g_dbus_proxy_call_sync ( + view->priv->web_extension, + "SelectIsEnabled", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(b)", &enabled); + g_variant_unref (result); + } + + if (enabled) { + g_dbus_proxy_call ( + view->priv->web_extension, + "EnableSelect", + g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + disable = TRUE; } - uid = webkit_dom_html_select_element_get_value ( - WEBKIT_DOM_HTML_SELECT_ELEMENT (select)); + result = g_dbus_proxy_call_sync ( + view->priv->web_extension, + "SelectGetValue", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); - source = e_source_registry_ref_source (view->priv->registry, uid); + if (result) { + const gchar *uid; - g_free (uid); + g_variant_get (result, "(&s)", &uid); + source = e_source_registry_ref_source (view->priv->registry, uid); + g_variant_unref (result); + } if (disable) { - webkit_dom_html_select_element_set_disabled ( - WEBKIT_DOM_HTML_SELECT_ELEMENT (select), TRUE); + g_dbus_proxy_call ( + view->priv->web_extension, + "EnableSelect", + g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE, FALSE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } - g_object_unref (select); return source; } @@ -2661,227 +2795,137 @@ void itip_view_set_rsvp (ItipView *view, gboolean rsvp) { - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return; - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_RSVP); - webkit_dom_html_input_element_set_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), rsvp); - g_object_unref (el); + input_set_checked (view, CHECKBOX_RSVP, rsvp); - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TEXTAREA_RSVP_COMMENT); - webkit_dom_html_text_area_element_set_disabled ( - WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp); - g_object_unref (el); + g_dbus_proxy_call ( + view->priv->web_extension, + "EnableTextArea", + g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, TEXTAREA_RSVP_COMMENT, !rsvp), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } gboolean itip_view_get_rsvp (ItipView *view) { - gboolean value; - WebKitDOMElement *el; - g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - if (!view->priv->dom_document) - return FALSE; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_RSVP); - value = webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el)); - g_object_unref (el); - return value; + return input_is_checked (view, CHECKBOX_RSVP); } void itip_view_set_show_rsvp_check (ItipView *view, gboolean show) { - WebKitDOMElement *label; - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) - return; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, "table_row_" CHECKBOX_RSVP); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_RSVP); - label = webkit_dom_element_get_next_element_sibling (el); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show); - g_object_unref (label); - - if (!show) { - webkit_dom_html_input_element_set_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE); - } - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_RSVP_COMMENT); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); - g_object_unref (el); + show_checkbox (view, CHECKBOX_RSVP, show, FALSE); + hide_element (view, TABLE_ROW_RSVP_COMMENT, !show); } gboolean itip_view_get_show_rsvp_check (ItipView *view) { - gboolean value; - WebKitDOMElement *el; - g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - if (!view->priv->dom_document) - return FALSE; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_RSVP); - value = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el)); - g_object_unref (el); - return !value; + return !element_is_hidden (view, CHECKBOX_RSVP); } void itip_view_set_update (ItipView *view, gboolean update) { - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) - return; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_UPDATE); - - webkit_dom_html_input_element_set_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), update); - g_object_unref (el); + input_set_checked (view, CHECKBOX_UPDATE, update); } gboolean itip_view_get_update (ItipView *view) { - gboolean value; - WebKitDOMElement *el; - g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - if (!view->priv->dom_document) - return FALSE; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_UPDATE); - value = webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el)); - g_object_unref (el); - return value; + return input_is_checked (view, CHECKBOX_UPDATE); } void itip_view_set_show_update_check (ItipView *view, gboolean show) { - WebKitDOMElement *label; - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) - return; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, "table_row_" CHECKBOX_UPDATE); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_UPDATE); - label = webkit_dom_element_get_next_element_sibling (el); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show); - g_object_unref (label); - - if (!show) { - webkit_dom_html_input_element_set_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE); - } - g_object_unref (el); + show_checkbox (view, CHECKBOX_UPDATE, show, FALSE); } gboolean itip_view_get_show_update_check (ItipView *view) { - gboolean value; - WebKitDOMElement *el; - g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - if (!view->priv->dom_document) - return FALSE; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_UPDATE); - value = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el)); - g_object_unref (el); - return !value; + return !element_is_hidden (view, CHECKBOX_UPDATE); } void itip_view_set_rsvp_comment (ItipView *view, const gchar *comment) { - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return; - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TEXTAREA_RSVP_COMMENT); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (el), (comment == NULL)); - if (comment) { - webkit_dom_html_text_area_element_set_value ( - WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), comment); + g_dbus_proxy_call ( + view->priv->web_extension, + "TextAreaSetValue", + g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, TEXTAREA_RSVP_COMMENT, comment), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } - g_object_unref (el); } gchar * itip_view_get_rsvp_comment (ItipView *view) { - gchar *value; - WebKitDOMElement *el; + GVariant *result; g_return_val_if_fail (ITIP_IS_VIEW (view), NULL); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return NULL; - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TEXTAREA_RSVP_COMMENT); - - if (webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el))) { + if (element_is_hidden (view, TEXTAREA_RSVP_COMMENT)) return NULL; + + result = g_dbus_proxy_call_sync ( + view->priv->web_extension, + "TextAreaGetValue", + g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, TEXTAREA_RSVP_COMMENT), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + gchar *value; + + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + return value; } - value = webkit_dom_html_text_area_element_get_value ( - WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el)); - g_object_unref (el); - return value; + return NULL; } void @@ -2897,77 +2941,24 @@ void itip_view_set_buttons_sensitive (ItipView *view, gboolean sensitive) { - WebKitDOMElement *el, *cell; - g_return_if_fail (ITIP_IS_VIEW (view)); d (printf ("Settings buttons %s\n", sensitive ? "sensitive" : "insensitive")); view->priv->buttons_sensitive = sensitive; - if (!view->priv->dom_document) + if (!view->priv->web_extension) return; - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_UPDATE); - webkit_dom_html_input_element_set_disabled ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_RECUR); - webkit_dom_html_input_element_set_disabled ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_FREE_TIME); - webkit_dom_html_input_element_set_disabled ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_KEEP_ALARM); - webkit_dom_html_input_element_set_disabled ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_INHERIT_ALARM); - webkit_dom_html_input_element_set_disabled ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_RSVP); - webkit_dom_html_input_element_set_disabled ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TEXTAREA_RSVP_COMMENT); - webkit_dom_html_text_area_element_set_disabled ( - WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, TABLE_ROW_BUTTONS); - cell = webkit_dom_element_get_first_element_child (el); - do { - WebKitDOMElement *btn, *next_cell; - - next_cell = webkit_dom_element_get_next_element_sibling (cell); - btn = webkit_dom_element_get_first_element_child (cell); - if (!webkit_dom_html_element_get_hidden ( - WEBKIT_DOM_HTML_ELEMENT (btn))) { - webkit_dom_html_button_element_set_disabled ( - WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive); - } - g_object_unref (btn); - g_object_unref (cell); - cell = next_cell; - } while (cell); - g_object_unref (el); + g_dbus_proxy_call ( + view->priv->web_extension, + "SetButtonsSensitive", + g_variant_new ("(tsb)", view->priv->page_id, view->priv->part_id, sensitive), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } gboolean @@ -2981,217 +2972,69 @@ itip_view_get_buttons_sensitive (ItipView *view) gboolean itip_view_get_recur_check_state (ItipView *view) { - gboolean value; - WebKitDOMElement *el; - g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - if (!view->priv->dom_document) - return FALSE; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_RECUR); - value = webkit_dom_html_input_element_get_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el)); - g_object_unref (el); - return value; + return input_is_checked (view, CHECKBOX_RECUR); } void itip_view_set_show_recur_check (ItipView *view, gboolean show) { - WebKitDOMElement *label; - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) - return; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, "table_row_" CHECKBOX_RECUR); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_RECUR); - label = webkit_dom_element_get_next_element_sibling (el); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show); - g_object_unref (label); - - if (!show) { - webkit_dom_html_input_element_set_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE); - } - - /* and update state of the second check */ - alarm_check_toggled_cb ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), - NULL, view); - g_object_unref (el); + show_checkbox (view, CHECKBOX_RECUR, show, TRUE); } void itip_view_set_show_free_time_check (ItipView *view, gboolean show) { - WebKitDOMElement *label; - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) - return; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, "table_row_" CHECKBOX_FREE_TIME); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_FREE_TIME); - label = webkit_dom_element_get_next_element_sibling (el); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show); - g_object_unref (label); - - if (!show) { - webkit_dom_html_input_element_set_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE); - } - - /* and update state of the second check */ - alarm_check_toggled_cb ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), - NULL, view); - g_object_unref (el); + show_checkbox (view, CHECKBOX_FREE_TIME, show, TRUE); } gboolean itip_view_get_free_time_check_state (ItipView *view) { - gboolean value; - WebKitDOMElement *el; - g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - if (!view->priv->dom_document) - return FALSE; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_FREE_TIME); - value = webkit_dom_html_input_element_get_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el)); - g_object_unref (el); - return value; + return input_is_checked (view, CHECKBOX_FREE_TIME); } void itip_view_set_show_keep_alarm_check (ItipView *view, gboolean show) { - WebKitDOMElement *label; - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) - return; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, "table_row_" CHECKBOX_KEEP_ALARM); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_KEEP_ALARM); - label = webkit_dom_element_get_next_element_sibling (el); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show); - g_object_unref (label); - - if (!show) { - webkit_dom_html_input_element_set_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE); - } - - /* and update state of the second check */ - alarm_check_toggled_cb ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), - NULL, view); - g_object_unref (el); + show_checkbox (view, CHECKBOX_KEEP_ALARM, show, TRUE); } gboolean itip_view_get_keep_alarm_check_state (ItipView *view) { - gboolean value; - WebKitDOMElement *el; - g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - if (!view->priv->dom_document) - return FALSE; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_KEEP_ALARM); - value = webkit_dom_html_input_element_get_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el)); - g_object_unref (el); - return value; + return input_is_checked (view, CHECKBOX_KEEP_ALARM); } void itip_view_set_show_inherit_alarm_check (ItipView *view, gboolean show) { - WebKitDOMElement *label; - WebKitDOMElement *el; - g_return_if_fail (ITIP_IS_VIEW (view)); - if (!view->priv->dom_document) - return; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, "table_row_" CHECKBOX_INHERIT_ALARM); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); - g_object_unref (el); - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_INHERIT_ALARM); - label = webkit_dom_element_get_next_element_sibling (el); - webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show); - g_object_unref (label); - - if (!show) { - webkit_dom_html_input_element_set_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE); - } - - /* and update state of the second check */ - alarm_check_toggled_cb ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el), - NULL, view); - g_object_unref (el); + show_checkbox (view, CHECKBOX_INHERIT_ALARM, show, TRUE); } gboolean itip_view_get_inherit_alarm_check_state (ItipView *view) { - gboolean value; - WebKitDOMElement *el; - g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - if (!view->priv->dom_document) - return FALSE; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, CHECKBOX_INHERIT_ALARM); - value = webkit_dom_html_input_element_get_checked ( - WEBKIT_DOM_HTML_INPUT_ELEMENT (el)); - g_object_unref (el); - return value; + return input_is_checked (view, CHECKBOX_INHERIT_ALARM); } void @@ -3199,7 +3042,6 @@ itip_view_set_error (ItipView *view, const gchar *error_html, gboolean show_save_btn) { - WebKitDOMElement *content, *error; GString *str; g_return_if_fail (ITIP_IS_VIEW (view)); @@ -3214,7 +3056,7 @@ itip_view_set_error (ItipView *view, "<tr width=\"100%\" id=\"" TABLE_ROW_BUTTONS "\">"); buttons_table_write_button ( - str, BUTTON_SAVE, _("Sa_ve"), + str, view->priv->itip_part_ptr, BUTTON_SAVE, _("Sa_ve"), "document-save", ITIP_VIEW_RESPONSE_SAVE); g_string_append (str, "</tr></table>"); @@ -3223,43 +3065,24 @@ itip_view_set_error (ItipView *view, view->priv->error = str->str; g_string_free (str, FALSE); - if (!view->priv->dom_document) + if (!view->priv->web_extension) return; - content = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, DIV_ITIP_CONTENT); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (content), TRUE); - g_object_unref (content); - - error = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, DIV_ITIP_ERROR); - webkit_dom_html_element_set_hidden ( - WEBKIT_DOM_HTML_ELEMENT (error), FALSE); - - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (error), view->priv->error, NULL); - g_object_unref (error); + hide_element (view, DIV_ITIP_CONTENT, TRUE); + hide_element (view, DIV_ITIP_ERROR, FALSE); + set_inner_html (view, DIV_ITIP_ERROR, view->priv->error); if (show_save_btn) { - WebKitDOMElement *el; - show_button (view, BUTTON_SAVE); + enable_button (view, BUTTON_SAVE, TRUE); - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, BUTTON_SAVE); - webkit_dom_html_button_element_set_disabled ( - WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE); - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (el), "click", - G_CALLBACK (button_clicked_cb), FALSE, view); + itip_view_register_clicked_listener (view); } } /******************************************************************************/ typedef struct { - EMailPartItip *puri; ItipView *view; GCancellable *itip_cancellable; GCancellable *cancellable; @@ -3350,7 +3173,6 @@ find_attendee_if_sentby (icalcomponent *ical_comp, static void find_to_address (ItipView *view, - EMailPartItip *itip_part, icalcomponent *ical_comp, icalparameter_partstat *status) { @@ -3362,26 +3184,26 @@ find_to_address (ItipView *view, registry = view->priv->registry; extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; - if (itip_part->to_address != NULL) + if (view->priv->to_address != NULL) return; - if (itip_part->msg != NULL && itip_part->folder != NULL) { + if (view->priv->message != NULL && view->priv->folder != NULL) { ESource *source; source = em_utils_guess_mail_identity ( - registry, itip_part->msg, - itip_part->folder, itip_part->uid); + registry, view->priv->message, + view->priv->folder, view->priv->message_uid); if (source != NULL) { extension = e_source_get_extension (source, extension_name); - itip_part->to_address = e_source_mail_identity_dup_address (extension); + view->priv->to_address = e_source_mail_identity_dup_address (extension); g_object_unref (source); } } - if (itip_part->to_address != NULL) + if (view->priv->to_address != NULL) return; /* Look through the list of attendees to find the user's address */ @@ -3403,20 +3225,20 @@ find_to_address (ItipView *view, param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER); if (param != NULL) - itip_part->to_name = g_strdup (icalparameter_get_cn (param)); + view->priv->to_name = g_strdup (icalparameter_get_cn (param)); text = icalproperty_get_value_as_string_r (prop); - itip_part->to_address = g_strdup (itip_strip_mailto (text)); + view->priv->to_address = g_strdup (itip_strip_mailto (text)); g_free (text); - g_strstrip (itip_part->to_address); + g_strstrip (view->priv->to_address); - itip_part->my_address = g_strdup (address); + view->priv->my_address = g_strdup (address); param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER); if (param != NULL && icalparameter_get_rsvp (param) == ICAL_RSVP_FALSE) - itip_part->no_reply_wanted = TRUE; + view->priv->no_reply_wanted = TRUE; if (status) { param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER); @@ -3428,7 +3250,7 @@ find_to_address (ItipView *view, g_list_free_full (list, (GDestroyNotify) g_object_unref); - if (itip_part->to_address != NULL) + if (view->priv->to_address != NULL) return; /* If the user's address was not found in the attendee's list, @@ -3461,20 +3283,20 @@ find_to_address (ItipView *view, param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER); if (param != NULL) - itip_part->to_name = g_strdup (icalparameter_get_cn (param)); + view->priv->to_name = g_strdup (icalparameter_get_cn (param)); text = icalproperty_get_value_as_string_r (prop); - itip_part->to_address = g_strdup (itip_strip_mailto (text)); + view->priv->to_address = g_strdup (itip_strip_mailto (text)); g_free (text); - g_strstrip (itip_part->to_address); + g_strstrip (view->priv->to_address); - itip_part->my_address = g_strdup (address); + view->priv->my_address = g_strdup (address); param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER); if (param != NULL && ICAL_RSVP_FALSE == icalparameter_get_rsvp (param)) - itip_part->no_reply_wanted = TRUE; + view->priv->no_reply_wanted = TRUE; if (status) { param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER); @@ -3489,7 +3311,6 @@ find_to_address (ItipView *view, static void find_from_address (ItipView *view, - EMailPartItip *pitip, icalcomponent *ical_comp) { ESourceRegistry *registry; @@ -3528,11 +3349,11 @@ find_from_address (ItipView *view, if (!(organizer_sentby_clean || organizer_clean)) return; - pitip->from_address = g_strdup (organizer_clean); + view->priv->from_address = g_strdup (organizer_clean); param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER); if (param) - pitip->from_name = g_strdup (icalparameter_get_cn (param)); + view->priv->from_name = g_strdup (icalparameter_get_cn (param)); extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; list = e_source_registry_list_enabled (registry, extension_name); @@ -3550,7 +3371,7 @@ find_from_address (ItipView *view, if ((organizer_clean && !g_ascii_strcasecmp (organizer_clean, address)) || (organizer_sentby_clean && !g_ascii_strcasecmp (organizer_sentby_clean, address))) { - pitip->my_address = g_strdup (address); + view->priv->my_address = g_strdup (address); break; } @@ -3563,14 +3384,14 @@ find_from_address (ItipView *view, } static ECalComponent * -get_real_item (EMailPartItip *pitip) +get_real_item (ItipView *view) { ECalComponent *comp = NULL; ESource *source; - source = e_client_get_source (E_CLIENT (pitip->current_client)); + source = e_client_get_source (E_CLIENT (view->priv->current_client)); if (source) - comp = g_hash_table_lookup (pitip->real_comps, e_source_get_uid (source)); + comp = g_hash_table_lookup (view->priv->real_comps, e_source_get_uid (source)); if (!comp) { return NULL; @@ -3580,12 +3401,12 @@ get_real_item (EMailPartItip *pitip) } static void -adjust_item (EMailPartItip *pitip, +adjust_item (ItipView *view, ECalComponent *comp) { ECalComponent *real_comp; - real_comp = get_real_item (pitip); + real_comp = get_real_item (view); if (real_comp != NULL) { ECalComponentText text; const gchar *string; @@ -3608,16 +3429,16 @@ adjust_item (EMailPartItip *pitip, } static gboolean -same_attendee_status (EMailPartItip *pitip, +same_attendee_status (ItipView *view, ECalComponent *received_comp) { ECalComponent *saved_comp; GSList *received_attendees = NULL, *saved_attendees = NULL, *riter, *siter; gboolean same = FALSE; - g_return_val_if_fail (pitip != NULL, FALSE); + g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE); - saved_comp = get_real_item (pitip); + saved_comp = get_real_item (view); if (!saved_comp) return FALSE; @@ -3662,31 +3483,22 @@ same_attendee_status (EMailPartItip *pitip, } static void -set_buttons_sensitive (EMailPartItip *pitip, - ItipView *view) +set_buttons_sensitive (ItipView *view) { - gboolean enabled = pitip->current_client != NULL; + gboolean enabled = view->priv->current_client != NULL; - if (enabled && pitip->current_client) - enabled = !e_client_is_readonly (E_CLIENT (pitip->current_client)); + if (enabled && view->priv->current_client) + enabled = !e_client_is_readonly (E_CLIENT (view->priv->current_client)); itip_view_set_buttons_sensitive (view, enabled); if (enabled && itip_view_get_mode (view) == ITIP_VIEW_MODE_REPLY && - pitip->comp && same_attendee_status (pitip, pitip->comp)) { + view->priv->comp && same_attendee_status (view, view->priv->comp)) { itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("Attendee status updated")); - if (view->priv->dom_document) { - WebKitDOMElement *el; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, BUTTON_UPDATE_ATTENDEE_STATUS); - webkit_dom_html_button_element_set_disabled ( - WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), TRUE); - g_object_unref (el); - } + enable_button (view, BUTTON_UPDATE_ATTENDEE_STATUS, FALSE); } } @@ -3707,12 +3519,10 @@ itip_view_cal_opened_cb (GObject *source_object, gpointer user_data) { ItipView *view; - EMailPartItip *pitip; EClient *client; GError *error = NULL; view = ITIP_VIEW (user_data); - pitip = itip_view_get_mail_part (view); client = e_client_cache_get_client_finish ( E_CLIENT_CACHE (source_object), result, &error); @@ -3737,13 +3547,13 @@ itip_view_cal_opened_cb (GObject *source_object, icalcomponent *icalcomp; gboolean show_recur_check; - icalcomp = e_cal_component_get_icalcomponent (pitip->comp); + icalcomp = e_cal_component_get_icalcomponent (view->priv->comp); show_recur_check = check_is_instance (icalcomp); itip_view_set_show_recur_check (view, show_recur_check); } - if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) { + if (view->priv->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) { gboolean needs_decline; needs_decline = e_client_check_capability ( @@ -3753,9 +3563,9 @@ itip_view_cal_opened_cb (GObject *source_object, itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH); } - pitip->current_client = g_object_ref (client); + view->priv->current_client = g_object_ref (client); - set_buttons_sensitive (pitip, view); + set_buttons_sensitive (view); exit: g_clear_object (&client); @@ -3763,8 +3573,7 @@ exit: } static void -start_calendar_server (EMailPartItip *pitip, - ItipView *view, +start_calendar_server (ItipView *view, ESource *source, ECalClientSourceType type, GAsyncReadyCallback func, @@ -3793,12 +3602,11 @@ start_calendar_server (EMailPartItip *pitip, e_client_cache_get_client ( client_cache, source, extension_name, 30, - pitip->cancellable, func, data); + view->priv->cancellable, func, data); } static void -start_calendar_server_by_uid (EMailPartItip *pitip, - ItipView *view, +start_calendar_server_by_uid (ItipView *view, const gchar *uid, ECalClientSourceType type) { @@ -3810,7 +3618,7 @@ start_calendar_server_by_uid (EMailPartItip *pitip, if (source != NULL) { start_calendar_server ( - pitip, view, source, type, + view, source, type, itip_view_cal_opened_cb, g_object_ref (view)); g_object_unref (source); @@ -3820,16 +3628,15 @@ start_calendar_server_by_uid (EMailPartItip *pitip, static void source_selected_cb (ItipView *view, ESource *source, - gpointer data) + gpointer user_data) { - EMailPartItip *pitip = data; + g_return_if_fail (ITIP_IS_VIEW (view)); + g_return_if_fail (E_IS_SOURCE (source)); itip_view_set_buttons_sensitive (view, FALSE); - g_return_if_fail (source != NULL); - start_calendar_server ( - pitip, view, source, pitip->type, + view, source, view->priv->type, itip_view_cal_opened_cb, g_object_ref (view)); } @@ -3838,13 +3645,11 @@ static void find_cal_update_ui (FormatItipFindData *fd, ECalClient *cal_client) { - EMailPartItip *pitip; ItipView *view; ESource *source; g_return_if_fail (fd != NULL); - pitip = fd->puri; view = fd->view; /* UI part gone */ @@ -3862,26 +3667,26 @@ find_cal_update_ui (FormatItipFindData *fd, } /* search for a master object if the detached object doesn't exist in the calendar */ - if (pitip->current_client && pitip->current_client == cal_client) { + if (view->priv->current_client && view->priv->current_client == cal_client) { const gchar *extension_name; gboolean rsvp_enabled = FALSE; itip_view_set_show_keep_alarm_check (view, fd->keep_alarm_check); - pitip->current_client = cal_client; + view->priv->current_client = cal_client; /* Provide extra info, since its not in the component */ /* FIXME Check sequence number of meeting? */ /* FIXME Do we need to adjust elsewhere for the delegated calendar item? */ /* FIXME Need to update the fields in the view now */ - if (pitip->method == ICAL_METHOD_REPLY || pitip->method == ICAL_METHOD_REFRESH) - adjust_item (pitip, pitip->comp); + if (view->priv->method == ICAL_METHOD_REPLY || view->priv->method == ICAL_METHOD_REFRESH) + adjust_item (view, view->priv->comp); /* We clear everything because we don't really care * about any other info/warnings now we found an * existing versions */ itip_view_clear_lower_info_items (view); - pitip->progress_info_id = 0; + view->priv->progress_info_id = 0; /* FIXME Check read only state of calendar? */ itip_view_add_lower_info_item_printf ( @@ -3894,21 +3699,21 @@ find_cal_update_ui (FormatItipFindData *fd, * invitiations (REQUEST), but not replies (REPLY). * Replies only make sense for events with an organizer. */ - if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) && - (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) && - pitip->has_organizer) { + if ((!view->priv->current_client || !e_cal_client_check_save_schedules (view->priv->current_client)) && + (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST) && + view->priv->has_organizer) { rsvp_enabled = TRUE; } itip_view_set_show_rsvp_check (view, rsvp_enabled); /* default is chosen in extract_itip_data() based on content of the VEVENT */ - itip_view_set_rsvp (view, !pitip->no_reply_wanted); + itip_view_set_rsvp (view, !view->priv->no_reply_wanted); - set_buttons_sensitive (pitip, view); + set_buttons_sensitive (view); g_cancellable_cancel (fd->cancellable); - switch (pitip->type) { + switch (view->priv->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: extension_name = E_SOURCE_EXTENSION_CALENDAR; break; @@ -3926,15 +3731,15 @@ find_cal_update_ui (FormatItipFindData *fd, g_signal_connect ( view, "source_selected", - G_CALLBACK (source_selected_cb), pitip); + G_CALLBACK (source_selected_cb), NULL); itip_view_set_source (view, source); - } else if (!pitip->current_client) + } else if (!view->priv->current_client) itip_view_set_show_keep_alarm_check (view, FALSE); - if (pitip->current_client && pitip->current_client == cal_client) { - if (e_cal_client_check_recurrences_no_master (pitip->current_client)) { - icalcomponent *icalcomp = e_cal_component_get_icalcomponent (pitip->comp); + if (view->priv->current_client && view->priv->current_client == cal_client) { + if (e_cal_client_check_recurrences_no_master (view->priv->current_client)) { + icalcomponent *icalcomp = e_cal_component_get_icalcomponent (view->priv->comp); if (check_is_instance (icalcomp)) itip_view_set_show_recur_check (view, TRUE); @@ -3942,9 +3747,9 @@ find_cal_update_ui (FormatItipFindData *fd, itip_view_set_show_recur_check (view, FALSE); } - if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) { + if (view->priv->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) { /* TODO The static capability should be made generic to convey that the calendar contains unaccepted items */ - if (e_client_check_capability (E_CLIENT (pitip->current_client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING)) + if (e_client_check_capability (E_CLIENT (view->priv->current_client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING)) itip_view_set_needs_decline (view, TRUE); else itip_view_set_needs_decline (view, FALSE); @@ -3964,11 +3769,10 @@ decrease_find_data (FormatItipFindData *fd) if (fd->count == 0 && !g_cancellable_is_cancelled (fd->cancellable)) { gboolean rsvp_enabled = FALSE; - EMailPartItip *pitip = fd->puri; ItipView *view = fd->view; - itip_view_remove_lower_info_item (view, pitip->progress_info_id); - pitip->progress_info_id = 0; + itip_view_remove_lower_info_item (view, view->priv->progress_info_id); + view->priv->progress_info_id = 0; /* * Only allow replies if backend doesn't do that automatically. @@ -3976,23 +3780,23 @@ decrease_find_data (FormatItipFindData *fd) * invitiations (REQUEST), but not replies (REPLY). * Replies only make sense for events with an organizer. */ - if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) && - (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) && - pitip->has_organizer) { + if ((!view->priv->current_client || !e_cal_client_check_save_schedules (view->priv->current_client)) && + (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST) && + view->priv->has_organizer) { rsvp_enabled = TRUE; } itip_view_set_show_rsvp_check (view, rsvp_enabled); /* default is chosen in extract_itip_data() based on content of the VEVENT */ - itip_view_set_rsvp (view, !pitip->no_reply_wanted); + itip_view_set_rsvp (view, !view->priv->no_reply_wanted); - if ((pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) - && !pitip->current_client) { + if ((view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST) + && !view->priv->current_client) { /* Reuse already declared one or rename? */ ESource *source = NULL; const gchar *extension_name; - switch (pitip->type) { + switch (view->priv->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: extension_name = E_SOURCE_EXTENSION_CALENDAR; break; @@ -4013,7 +3817,7 @@ decrease_find_data (FormatItipFindData *fd) g_signal_connect ( view, "source_selected", - G_CALLBACK (source_selected_cb), pitip); + G_CALLBACK (source_selected_cb), NULL); if (source != NULL) { itip_view_set_source (view, source); @@ -4024,8 +3828,8 @@ decrease_find_data (FormatItipFindData *fd) itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to find any calendars")); itip_view_set_buttons_sensitive (view, FALSE); } - } else if (!pitip->current_client) { - switch (pitip->type) { + } else if (!view->priv->current_client) { + switch (view->priv->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: itip_view_add_lower_info_item_printf ( view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING, @@ -4087,8 +3891,8 @@ get_object_without_rid_ready_cb (GObject *source_object, if (icalcomp) { ECalComponent *comp; - fd->puri->current_client = cal_client; - fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method == ICAL_METHOD_REQUEST) && + fd->view->priv->current_client = cal_client; + fd->keep_alarm_check = (fd->view->priv->method == ICAL_METHOD_PUBLISH || fd->view->priv->method == ICAL_METHOD_REQUEST) && (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) || icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) || icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) || @@ -4099,7 +3903,7 @@ get_object_without_rid_ready_cb (GObject *source_object, if (comp) { ESource *source = e_client_get_source (E_CLIENT (cal_client)); - g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp); + g_hash_table_insert (fd->view->priv->real_comps, g_strdup (e_source_get_uid (source)), comp); } find_cal_update_ui (fd, cal_client); @@ -4136,8 +3940,8 @@ get_object_with_rid_ready_cb (GObject *source_object, if (icalcomp) { ECalComponent *comp; - fd->puri->current_client = cal_client; - fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method == ICAL_METHOD_REQUEST) && + fd->view->priv->current_client = cal_client; + fd->keep_alarm_check = (fd->view->priv->method == ICAL_METHOD_PUBLISH || fd->view->priv->method == ICAL_METHOD_REQUEST) && (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) || icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) || icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) || @@ -4148,7 +3952,7 @@ get_object_with_rid_ready_cb (GObject *source_object, if (comp) { ESource *source = e_client_get_source (E_CLIENT (cal_client)); - g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp); + g_hash_table_insert (fd->view->priv->real_comps, g_strdup (e_source_get_uid (source)), comp); } find_cal_update_ui (fd, cal_client); @@ -4210,7 +4014,6 @@ find_cal_opened_cb (GObject *source_object, gpointer user_data) { FormatItipFindData *fd = user_data; - EMailPartItip *pitip = fd->puri; ItipView *view = fd->view; EClient *client; ESource *source; @@ -4260,7 +4063,7 @@ find_cal_opened_cb (GObject *source_object, extension = e_source_get_extension (source, extension_name); search_for_conflicts = - (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) && + (view->priv->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) && e_source_conflict_search_get_include_me (extension); } @@ -4282,7 +4085,7 @@ find_cal_opened_cb (GObject *source_object, return; } - if (!pitip->current_client) { + if (!view->priv->current_client) { e_cal_client_get_object ( cal_client, fd->uid, fd->rid, fd->cancellable, @@ -4302,8 +4105,7 @@ itip_cancellable_cancelled (GCancellable *itip_cancellable, } static void -find_server (EMailPartItip *pitip, - ItipView *view, +find_server (ItipView *view, ECalComponent *comp) { FormatItipFindData *fd = NULL; @@ -4316,9 +4118,10 @@ find_server (EMailPartItip *pitip, const gchar *extension_name; const gchar *store_uid; - g_return_if_fail (pitip->folder != NULL); + g_return_if_fail (ITIP_IS_VIEW (view)); + g_return_if_fail (view->priv->folder != NULL); - switch (pitip->type) { + switch (view->priv->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: extension_name = E_SOURCE_EXTENSION_CALENDAR; break; @@ -4341,7 +4144,7 @@ find_server (EMailPartItip *pitip, /* XXX Not sure what this was trying to do, * but it propbably doesn't work anymore. * Some comments would have been helpful. */ - parent_store = camel_folder_get_parent_store (pitip->folder); + parent_store = camel_folder_get_parent_store (view->priv->folder); store_uid = camel_service_get_uid (CAMEL_SERVICE (parent_store)); @@ -4382,12 +4185,12 @@ find_server (EMailPartItip *pitip, if (current_source) { link = conflict_list; - pitip->progress_info_id = itip_view_add_lower_info_item ( + view->priv->progress_info_id = itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS, _("Opening the calendar. Please wait...")); } else { link = list; - pitip->progress_info_id = itip_view_add_lower_info_item ( + view->priv->progress_info_id = itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS, _("Searching for an existing version of this appointment")); } @@ -4399,9 +4202,8 @@ find_server (EMailPartItip *pitip, gchar *start = NULL, *end = NULL; fd = g_new0 (FormatItipFindData, 1); - fd->puri = pitip; fd->view = g_object_ref (view); - fd->itip_cancellable = g_object_ref (pitip->cancellable); + fd->itip_cancellable = g_object_ref (view->priv->cancellable); fd->cancellable = g_cancellable_new (); fd->cancelled_id = g_cancellable_connect ( fd->itip_cancellable, @@ -4412,9 +4214,9 @@ find_server (EMailPartItip *pitip, /* avoid free this at the end */ rid = NULL; - if (pitip->start_time && pitip->end_time) { - start = isodate_from_time_t (pitip->start_time); - end = isodate_from_time_t (pitip->end_time); + if (view->priv->start_time && view->priv->end_time) { + start = isodate_from_time_t (view->priv->start_time); + end = isodate_from_time_t (view->priv->end_time); fd->sexp = g_strdup_printf ( "(and (occur-in-time-range? " @@ -4422,7 +4224,7 @@ find_server (EMailPartItip *pitip, "(make-time \"%s\")) " "(not (uid? \"%s\")))", start, end, - icalcomponent_get_uid (pitip->ical_comp)); + icalcomponent_get_uid (view->priv->ical_comp)); } g_free (start); @@ -4432,7 +4234,7 @@ find_server (EMailPartItip *pitip, d (printf ("Increasing itip formatter search count to %d\n", fd->count)); start_calendar_server ( - pitip, view, source, pitip->type, + view, source, view->priv->type, find_cal_opened_cb, fd); } @@ -4633,26 +4435,25 @@ get_uri_for_part (CamelMimePart *mime_part) } static void -update_item_progress_info (EMailPartItip *pitip, - ItipView *view, +update_item_progress_info (ItipView *view, const gchar *message) { - if (pitip->update_item_progress_info_id) { - itip_view_remove_lower_info_item (view, pitip->update_item_progress_info_id); - pitip->update_item_progress_info_id = 0; + if (view->priv->update_item_progress_info_id) { + itip_view_remove_lower_info_item (view, view->priv->update_item_progress_info_id); + view->priv->update_item_progress_info_id = 0; if (!message) itip_view_set_buttons_sensitive (view, TRUE); } - if (pitip->update_item_error_info_id) { - itip_view_remove_lower_info_item (view, pitip->update_item_error_info_id); - pitip->update_item_error_info_id = 0; + if (view->priv->update_item_error_info_id) { + itip_view_remove_lower_info_item (view, view->priv->update_item_error_info_id); + view->priv->update_item_error_info_id = 0; } if (message) { itip_view_set_buttons_sensitive (view, FALSE); - pitip->update_item_progress_info_id = + view->priv->update_item_progress_info_id = itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS, @@ -4660,13 +4461,25 @@ update_item_progress_info (EMailPartItip *pitip, } } +static gboolean +itip_view_get_delete_message (void) +{ + GSettings *settings; + gboolean delete_message; + + settings = e_util_ref_settings ("org.gnome.evolution.plugin.itip"); + delete_message = g_settings_get_boolean (settings, "delete-processed"); + g_clear_object (&settings); + + return delete_message; +} + static void -finish_message_delete_with_rsvp (EMailPartItip *pitip, - ItipView *view, +finish_message_delete_with_rsvp (ItipView *view, ECalClient *client) { - if (pitip->delete_message && pitip->folder) - camel_folder_delete_message (pitip->folder, pitip->uid); + if (itip_view_get_delete_message () && view->priv->folder) + camel_folder_delete_message (view->priv->folder, view->priv->message_uid); if (itip_view_get_rsvp (view)) { ECalComponent *comp = NULL; @@ -4678,13 +4491,13 @@ finish_message_delete_with_rsvp (EMailPartItip *pitip, GSList *l, *list = NULL; gboolean found; - comp = e_cal_component_clone (pitip->comp); + comp = e_cal_component_clone (view->priv->comp); if (comp == NULL) return; - if (pitip->to_address == NULL) - find_to_address (view, pitip, pitip->ical_comp, NULL); - g_return_if_fail (pitip->to_address != NULL); + if (view->priv->to_address == NULL) + find_to_address (view, view->priv->ical_comp, NULL); + g_return_if_fail (view->priv->to_address != NULL); ical_comp = e_cal_component_get_icalcomponent (comp); @@ -4706,9 +4519,9 @@ finish_message_delete_with_rsvp (EMailPartItip *pitip, /* We do this to ensure there is at most one * attendee in the response */ - if (found || g_ascii_strcasecmp (pitip->to_address, text)) + if (found || g_ascii_strcasecmp (view->priv->to_address, text)) list = g_slist_prepend (list, prop); - else if (!g_ascii_strcasecmp (pitip->to_address, text)) + else if (!g_ascii_strcasecmp (view->priv->to_address, text)) found = TRUE; g_free (text); } @@ -4742,11 +4555,11 @@ finish_message_delete_with_rsvp (EMailPartItip *pitip, if (itip_send_comp_sync ( view->priv->registry, E_CAL_COMPONENT_METHOD_REPLY, - comp, pitip->current_client, - pitip->top_level, NULL, NULL, TRUE, FALSE, NULL, NULL) && - pitip->folder) { + comp, view->priv->current_client, + view->priv->top_level, NULL, NULL, TRUE, FALSE, NULL, NULL) && + view->priv->folder) { camel_folder_set_message_flags ( - pitip->folder, pitip->uid, + view->priv->folder, view->priv->message_uid, CAMEL_MESSAGE_ANSWERED, CAMEL_MESSAGE_ANSWERED); } @@ -4754,7 +4567,7 @@ finish_message_delete_with_rsvp (EMailPartItip *pitip, g_object_unref (comp); } - update_item_progress_info (pitip, view, NULL); + update_item_progress_info (view, NULL); } static void @@ -4765,7 +4578,6 @@ receive_objects_ready_cb (GObject *ecalclient, ECalClient *client = E_CAL_CLIENT (ecalclient); ESource *source = e_client_get_source (E_CLIENT (client)); ItipView *view = user_data; - EMailPartItip *pitip = itip_view_get_mail_part (view); GError *error = NULL; e_cal_client_receive_objects_finish (client, result, &error); @@ -4775,8 +4587,8 @@ receive_objects_ready_cb (GObject *ecalclient, return; } else if (error != NULL) { - update_item_progress_info (pitip, view, NULL); - pitip->update_item_error_info_id = + update_item_progress_info (view, NULL); + view->priv->update_item_error_info_id = itip_view_add_lower_info_item_printf ( view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("Unable to send item to calendar '%s'. %s"), @@ -4790,7 +4602,7 @@ receive_objects_ready_cb (GObject *ecalclient, itip_view_clear_lower_info_items (view); - switch (pitip->update_item_response) { + switch (view->priv->update_item_response) { case ITIP_VIEW_RESPONSE_ACCEPT: itip_view_add_lower_info_item_printf ( view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, @@ -4818,12 +4630,11 @@ receive_objects_ready_cb (GObject *ecalclient, break; } - finish_message_delete_with_rsvp (pitip, view, client); + finish_message_delete_with_rsvp (view, client); } static void -update_item (EMailPartItip *pitip, - ItipView *view, +update_item (ItipView *view, ItipViewResponse response) { struct icaltimetype stamp; @@ -4832,7 +4643,7 @@ update_item (EMailPartItip *pitip, ECalComponent *clone_comp; gchar *str; - update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait...")); + update_item_progress_info (view, _("Saving changes to the calendar. Please wait...")); /* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which * the user accepted/declined the request. (Outlook ignores @@ -4848,11 +4659,11 @@ update_item (EMailPartItip *pitip, prop = icalproperty_new_x (str); g_free (str); icalproperty_set_x_name (prop, "X-MICROSOFT-CDO-REPLYTIME"); - icalcomponent_add_property (pitip->ical_comp, prop); + icalcomponent_add_property (view->priv->ical_comp, prop); - clone = icalcomponent_new_clone (pitip->ical_comp); - icalcomponent_add_component (pitip->top_level, clone); - icalcomponent_set_method (pitip->top_level, pitip->method); + clone = icalcomponent_new_clone (view->priv->ical_comp); + icalcomponent_add_component (view->priv->top_level, clone); + icalcomponent_set_method (view->priv->top_level, view->priv->method); if (!itip_view_get_inherit_alarm_check_state (view)) { icalcomponent *alarm_comp; @@ -4869,8 +4680,8 @@ update_item (EMailPartItip *pitip, clone_comp = e_cal_component_new (); if (!e_cal_component_set_icalcomponent (clone_comp, clone)) { - update_item_progress_info (pitip, view, NULL); - pitip->update_item_error_info_id = + update_item_progress_info (view, NULL); + view->priv->update_item_error_info_id = itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to parse item")); @@ -4882,7 +4693,7 @@ update_item (EMailPartItip *pitip, GList *alarms, *l; ECalComponentAlarm *alarm; - real_comp = get_real_item (pitip); + real_comp = get_real_item (view); if (real_comp != NULL) { alarms = e_cal_component_get_alarm_uids (real_comp); @@ -4910,7 +4721,7 @@ update_item (EMailPartItip *pitip, if ((response != ITIP_VIEW_RESPONSE_CANCEL) && (response != ITIP_VIEW_RESPONSE_DECLINE)) { GSList *attachments = NULL, *new_attachments = NULL, *l; - CamelMimeMessage *msg = pitip->msg; + CamelMimeMessage *msg = view->priv->message; e_cal_component_get_attachment_list (clone_comp, &attachments); @@ -4929,7 +4740,7 @@ update_item (EMailPartItip *pitip, /* Skip the actual message and the text/calendar part */ /* FIXME Do we need to skip anything else? */ - if (part == (CamelMimePart *) msg || part == pitip->part) + if (part == (CamelMimePart *) msg || part == view->priv->itip_mime_part) continue; new_uri = get_uri_for_part (part); @@ -4959,17 +4770,17 @@ update_item (EMailPartItip *pitip, e_cal_component_set_attachment_list (clone_comp, new_attachments); } - pitip->update_item_response = response; + view->priv->update_item_response = response; e_cal_client_receive_objects ( - pitip->current_client, - pitip->top_level, - pitip->cancellable, + view->priv->current_client, + view->priv->top_level, + view->priv->cancellable, receive_objects_ready_cb, view); cleanup: - icalcomponent_remove_component (pitip->top_level, clone); + icalcomponent_remove_component (view->priv->top_level, clone); g_object_unref (clone_comp); } @@ -5056,8 +4867,7 @@ send_comp_to_attendee (ESourceRegistry *registry, } static void -remove_delegate (EMailPartItip *pitip, - ItipView *view, +remove_delegate (ItipView *view, const gchar *delegate, const gchar *delegator, ECalComponent *comp) @@ -5072,13 +4882,13 @@ remove_delegate (EMailPartItip *pitip, /* send cancellation notice to delegate */ status = send_comp_to_attendee ( view->priv->registry, - E_CAL_COMPONENT_METHOD_CANCEL, pitip->comp, - delegate, pitip->current_client, comment); + E_CAL_COMPONENT_METHOD_CANCEL, view->priv->comp, + delegate, view->priv->current_client, comment); if (status != 0) { send_comp_to_attendee ( view->priv->registry, - E_CAL_COMPONENT_METHOD_REQUEST, pitip->comp, - delegator, pitip->current_client, comment); + E_CAL_COMPONENT_METHOD_REQUEST, view->priv->comp, + delegator, view->priv->current_client, comment); } if (status != 0) { itip_view_add_lower_info_item ( @@ -5095,10 +4905,10 @@ remove_delegate (EMailPartItip *pitip, } static void -update_x (ECalComponent *pitip_comp, +update_x (ECalComponent *view_comp, ECalComponent *comp) { - icalcomponent *itip_icalcomp = e_cal_component_get_icalcomponent (pitip_comp); + icalcomponent *itip_icalcomp = e_cal_component_get_icalcomponent (view_comp); icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp); icalproperty *prop = icalcomponent_get_first_property (itip_icalcomp, ICAL_X_PROPERTY); @@ -5122,7 +4932,6 @@ modify_object_cb (GObject *ecalclient, { ECalClient *client = E_CAL_CLIENT (ecalclient); ItipView *view = user_data; - EMailPartItip *pitip = itip_view_get_mail_part (view); GError *error = NULL; e_cal_client_modify_object_finish (client, result, &error); @@ -5131,8 +4940,8 @@ modify_object_cb (GObject *ecalclient, g_error_free (error); } else if (error != NULL) { - update_item_progress_info (pitip, view, NULL); - pitip->update_item_error_info_id = + update_item_progress_info (view, NULL); + view->priv->update_item_error_info_id = itip_view_add_lower_info_item_printf ( view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to update attendee. %s"), @@ -5140,29 +4949,20 @@ modify_object_cb (GObject *ecalclient, g_error_free (error); } else { - update_item_progress_info (pitip, view, NULL); + update_item_progress_info (view, NULL); itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("Attendee status updated")); - if (view->priv->dom_document) { - WebKitDOMElement *el; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, BUTTON_UPDATE_ATTENDEE_STATUS); - webkit_dom_html_button_element_set_disabled ( - WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), TRUE); - g_object_unref (el); - } + enable_button (view, BUTTON_UPDATE_ATTENDEE_STATUS, FALSE); - if (pitip->delete_message && pitip->folder) - camel_folder_delete_message (pitip->folder, pitip->uid); + if (itip_view_get_delete_message () && view->priv->folder) + camel_folder_delete_message (view->priv->folder, view->priv->message_uid); } } static void -update_attendee_status_icalcomp (EMailPartItip *pitip, - ItipView *view, +update_attendee_status_icalcomp (ItipView *view, icalcomponent *icalcomp) { ECalComponent *comp; @@ -5170,8 +4970,8 @@ update_attendee_status_icalcomp (EMailPartItip *pitip, gchar *rid; GSList *attendees; - e_cal_component_get_uid (pitip->comp, &uid); - rid = e_cal_component_get_recurid_as_string (pitip->comp); + e_cal_component_get_uid (view->priv->comp, &uid); + rid = e_cal_component_get_recurid_as_string (view->priv->comp); comp = e_cal_component_new (); if (!e_cal_component_set_icalcomponent (comp, icalcomp)) { @@ -5184,9 +4984,9 @@ update_attendee_status_icalcomp (EMailPartItip *pitip, icalcomponent *org_icalcomp; const gchar *delegate; - org_icalcomp = e_cal_component_get_icalcomponent (pitip->comp); + org_icalcomp = e_cal_component_get_icalcomponent (view->priv->comp); - e_cal_component_get_attendee_list (pitip->comp, &attendees); + e_cal_component_get_attendee_list (view->priv->comp, &attendees); if (attendees != NULL) { ECalComponentAttendee *a = attendees->data; icalproperty *prop, *del_prop; @@ -5205,7 +5005,7 @@ update_attendee_status_icalcomp (EMailPartItip *pitip, icalcomponent_add_property (icalcomp, icalproperty_new_clone (del_prop)); e_cal_component_rescan (comp); } else if (response == GTK_RESPONSE_NO) { - remove_delegate (pitip, view, delegate, itip_strip_mailto (a->value), comp); + remove_delegate (view, delegate, itip_strip_mailto (a->value), comp); goto cleanup; } else { goto cleanup; @@ -5228,7 +5028,6 @@ update_attendee_status_icalcomp (EMailPartItip *pitip, e_cal_component_rescan (comp); } else if (response == GTK_RESPONSE_NO) { remove_delegate ( - pitip, view, itip_strip_mailto (a->value), itip_strip_mailto (a->delfrom), @@ -5279,23 +5078,23 @@ update_attendee_status_icalcomp (EMailPartItip *pitip, } } - update_x (pitip->comp, comp); + update_x (view->priv->comp, comp); if (itip_view_get_update (view)) { e_cal_component_commit_sequence (comp); itip_send_comp_sync ( view->priv->registry, E_CAL_COMPONENT_METHOD_REQUEST, - comp, pitip->current_client, + comp, view->priv->current_client, NULL, NULL, NULL, TRUE, FALSE, NULL, NULL); } - update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait...")); + update_item_progress_info (view, _("Saving changes to the calendar. Please wait...")); e_cal_client_modify_object ( - pitip->current_client, + view->priv->current_client, icalcomp, rid ? E_CAL_OBJ_MOD_THIS : E_CAL_OBJ_MOD_ALL, - pitip->cancellable, + view->priv->cancellable, modify_object_cb, view); @@ -5310,7 +5109,6 @@ update_attendee_status_get_object_without_rid_cb (GObject *ecalclient, { ECalClient *client = E_CAL_CLIENT (ecalclient); ItipView *view = user_data; - EMailPartItip *pitip = itip_view_get_mail_part (view); icalcomponent *icalcomp = NULL; GError *error = NULL; @@ -5322,8 +5120,8 @@ update_attendee_status_get_object_without_rid_cb (GObject *ecalclient, } else if (error != NULL) { g_error_free (error); - update_item_progress_info (pitip, view, NULL); - pitip->update_item_error_info_id = + update_item_progress_info (view, NULL); + view->priv->update_item_error_info_id = itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING, @@ -5331,7 +5129,7 @@ update_attendee_status_get_object_without_rid_cb (GObject *ecalclient, "because the item no longer exists")); } else { - update_attendee_status_icalcomp (pitip, view, icalcomp); + update_attendee_status_icalcomp (view, icalcomp); } } @@ -5342,7 +5140,6 @@ update_attendee_status_get_object_with_rid_cb (GObject *ecalclient, { ECalClient *client = E_CAL_CLIENT (ecalclient); ItipView *view = user_data; - EMailPartItip *pitip = itip_view_get_mail_part (view); icalcomponent *icalcomp = NULL; GError *error = NULL; @@ -5357,12 +5154,12 @@ update_attendee_status_get_object_with_rid_cb (GObject *ecalclient, g_error_free (error); - e_cal_component_get_uid (pitip->comp, &uid); - rid = e_cal_component_get_recurid_as_string (pitip->comp); + e_cal_component_get_uid (view->priv->comp, &uid); + rid = e_cal_component_get_recurid_as_string (view->priv->comp); if (rid == NULL || *rid == '\0') { - update_item_progress_info (pitip, view, NULL); - pitip->update_item_error_info_id = + update_item_progress_info (view, NULL); + view->priv->update_item_error_info_id = itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING, @@ -5370,10 +5167,10 @@ update_attendee_status_get_object_with_rid_cb (GObject *ecalclient, "because the item no longer exists")); } else { e_cal_client_get_object ( - pitip->current_client, + view->priv->current_client, uid, NULL, - pitip->cancellable, + view->priv->cancellable, update_attendee_status_get_object_without_rid_cb, view); } @@ -5381,28 +5178,27 @@ update_attendee_status_get_object_with_rid_cb (GObject *ecalclient, g_free (rid); } else { - update_attendee_status_icalcomp (pitip, view, icalcomp); + update_attendee_status_icalcomp (view, icalcomp); } } static void -update_attendee_status (EMailPartItip *pitip, - ItipView *view) +update_attendee_status (ItipView *view) { const gchar *uid = NULL; gchar *rid; /* Obtain our version */ - e_cal_component_get_uid (pitip->comp, &uid); - rid = e_cal_component_get_recurid_as_string (pitip->comp); + e_cal_component_get_uid (view->priv->comp, &uid); + rid = e_cal_component_get_recurid_as_string (view->priv->comp); - update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait...")); + update_item_progress_info (view, _("Saving changes to the calendar. Please wait...")); /* search for a master object if the detached object doesn't exist in the calendar */ e_cal_client_get_object ( - pitip->current_client, + view->priv->current_client, uid, rid, - pitip->cancellable, + view->priv->cancellable, update_attendee_status_get_object_with_rid_cb, view); @@ -5410,22 +5206,21 @@ update_attendee_status (EMailPartItip *pitip, } static void -send_item (EMailPartItip *pitip, - ItipView *view) +send_item (ItipView *view) { ECalComponent *comp; - comp = get_real_item (pitip); + comp = get_real_item (view); if (comp != NULL) { itip_send_comp_sync ( view->priv->registry, E_CAL_COMPONENT_METHOD_REQUEST, - comp, pitip->current_client, + comp, view->priv->current_client, NULL, NULL, NULL, TRUE, FALSE, NULL, NULL); g_object_unref (comp); - switch (pitip->type) { + switch (view->priv->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, @@ -5446,7 +5241,7 @@ send_item (EMailPartItip *pitip, break; } } else { - switch (pitip->type) { + switch (view->priv->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: itip_view_add_lower_info_item ( view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, @@ -5511,18 +5306,18 @@ attachment_load_finish (EAttachment *attachment, } static void -save_vcalendar_cb (EMailPartItip *pitip) +save_vcalendar_cb (ItipView *view) { EAttachment *attachment; EShell *shell; GFile *file; const gchar *suggestion; - g_return_if_fail (pitip != NULL); - g_return_if_fail (pitip->vcalendar != NULL); - g_return_if_fail (pitip->part != NULL); + g_return_if_fail (ITIP_IS_VIEW (view)); + g_return_if_fail (view->priv->vcalendar != NULL); + g_return_if_fail (view->priv->itip_mime_part != NULL); - suggestion = camel_mime_part_get_filename (pitip->part); + suggestion = camel_mime_part_get_filename (view->priv->itip_mime_part); if (suggestion == NULL) { /* Translators: This is a default filename for a calendar. */ suggestion = _("calendar.ics"); @@ -5535,7 +5330,7 @@ save_vcalendar_cb (EMailPartItip *pitip) return; attachment = e_attachment_new (); - e_attachment_set_mime_part (attachment, pitip->part); + e_attachment_set_mime_part (attachment, view->priv->itip_mime_part); e_attachment_load_async ( attachment, (GAsyncReadyCallback) @@ -5562,8 +5357,7 @@ set_itip_error (ItipView *view, } static gboolean -extract_itip_data (EMailPartItip *pitip, - ItipView *view, +extract_itip_data (ItipView *view, gboolean *have_alarms) { GSettings *settings; @@ -5576,7 +5370,7 @@ extract_itip_data (EMailPartItip *pitip, ECalComponent *comp; gboolean use_default_reminder; - if (!pitip->vcalendar) { + if (!view->priv->vcalendar) { set_itip_error ( view, _("The calendar attached is not valid"), @@ -5586,53 +5380,53 @@ extract_itip_data (EMailPartItip *pitip, return FALSE; } - pitip->top_level = e_cal_util_new_top_level (); + view->priv->top_level = e_cal_util_new_top_level (); - pitip->main_comp = icalparser_parse_string (pitip->vcalendar); - if (pitip->main_comp == NULL || !is_icalcomp_valid (pitip->main_comp)) { + view->priv->main_comp = icalparser_parse_string (view->priv->vcalendar); + if (view->priv->main_comp == NULL || !is_icalcomp_valid (view->priv->main_comp)) { set_itip_error ( view, _("The calendar attached is not valid"), _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."), FALSE); - if (pitip->main_comp) { - icalcomponent_free (pitip->main_comp); - pitip->main_comp = NULL; + if (view->priv->main_comp) { + icalcomponent_free (view->priv->main_comp); + view->priv->main_comp = NULL; } return FALSE; } - prop = icalcomponent_get_first_property (pitip->main_comp, ICAL_METHOD_PROPERTY); + prop = icalcomponent_get_first_property (view->priv->main_comp, ICAL_METHOD_PROPERTY); if (prop == NULL) { - pitip->method = ICAL_METHOD_PUBLISH; + view->priv->method = ICAL_METHOD_PUBLISH; } else { - pitip->method = icalproperty_get_method (prop); + view->priv->method = icalproperty_get_method (prop); } - tz_iter = icalcomponent_begin_component (pitip->main_comp, ICAL_VTIMEZONE_COMPONENT); + tz_iter = icalcomponent_begin_component (view->priv->main_comp, ICAL_VTIMEZONE_COMPONENT); while ((tz_comp = icalcompiter_deref (&tz_iter)) != NULL) { icalcomponent *clone; clone = icalcomponent_new_clone (tz_comp); - icalcomponent_add_component (pitip->top_level, clone); + icalcomponent_add_component (view->priv->top_level, clone); icalcompiter_next (&tz_iter); } - pitip->iter = icalcomponent_begin_component (pitip->main_comp, ICAL_ANY_COMPONENT); - pitip->ical_comp = icalcompiter_deref (&pitip->iter); - if (pitip->ical_comp != NULL) { - kind = icalcomponent_isa (pitip->ical_comp); + view->priv->iter = icalcomponent_begin_component (view->priv->main_comp, ICAL_ANY_COMPONENT); + view->priv->ical_comp = icalcompiter_deref (&view->priv->iter); + if (view->priv->ical_comp != NULL) { + kind = icalcomponent_isa (view->priv->ical_comp); if (kind != ICAL_VEVENT_COMPONENT && kind != ICAL_VTODO_COMPONENT && kind != ICAL_VFREEBUSY_COMPONENT && kind != ICAL_VJOURNAL_COMPONENT) - pitip->ical_comp = get_next (&pitip->iter); + view->priv->ical_comp = get_next (&view->priv->iter); } - if (pitip->ical_comp == NULL) { + if (view->priv->ical_comp == NULL) { set_itip_error ( view, _("The item in the calendar is not valid"), @@ -5642,13 +5436,13 @@ extract_itip_data (EMailPartItip *pitip, return FALSE; } - switch (icalcomponent_isa (pitip->ical_comp)) { + switch (icalcomponent_isa (view->priv->ical_comp)) { case ICAL_VEVENT_COMPONENT: - pitip->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; - pitip->has_organizer = icalcomponent_get_first_property (pitip->ical_comp, ICAL_ORGANIZER_PROPERTY) != NULL; - if (icalcomponent_get_first_property (pitip->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) { + view->priv->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; + view->priv->has_organizer = icalcomponent_get_first_property (view->priv->ical_comp, ICAL_ORGANIZER_PROPERTY) != NULL; + if (icalcomponent_get_first_property (view->priv->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) { /* no attendees: assume that that this is not a meeting and organizer doesn't want a reply */ - pitip->no_reply_wanted = TRUE; + view->priv->no_reply_wanted = TRUE; } else { /* * if we have attendees, then find_to_address() will check for our RSVP @@ -5657,10 +5451,10 @@ extract_itip_data (EMailPartItip *pitip, } break; case ICAL_VTODO_COMPONENT: - pitip->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; + view->priv->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; break; case ICAL_VJOURNAL_COMPONENT: - pitip->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS; + view->priv->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS; break; default: set_itip_error ( @@ -5672,12 +5466,12 @@ extract_itip_data (EMailPartItip *pitip, return FALSE; } - pitip->total = icalcomponent_count_components (pitip->main_comp, ICAL_VEVENT_COMPONENT); - pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VTODO_COMPONENT); - pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VFREEBUSY_COMPONENT); - pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VJOURNAL_COMPONENT); + view->priv->total = icalcomponent_count_components (view->priv->main_comp, ICAL_VEVENT_COMPONENT); + view->priv->total += icalcomponent_count_components (view->priv->main_comp, ICAL_VTODO_COMPONENT); + view->priv->total += icalcomponent_count_components (view->priv->main_comp, ICAL_VFREEBUSY_COMPONENT); + view->priv->total += icalcomponent_count_components (view->priv->main_comp, ICAL_VJOURNAL_COMPONENT); - if (pitip->total > 1) { + if (view->priv->total > 1) { set_itip_error ( view, @@ -5685,27 +5479,29 @@ extract_itip_data (EMailPartItip *pitip, _("To process all of these items, the file should be saved and the calendar imported"), TRUE); - } if (pitip->total > 0) { - pitip->current = 1; + } + + if (view->priv->total > 0) { + view->priv->current = 1; } else { - pitip->current = 0; + view->priv->current = 0; } - if (icalcomponent_isa (pitip->ical_comp) != ICAL_VJOURNAL_COMPONENT) { + if (icalcomponent_isa (view->priv->ical_comp) != ICAL_VJOURNAL_COMPONENT) { gchar *my_address; prop = NULL; comp = e_cal_component_new (); - e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (pitip->ical_comp)); + e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (view->priv->ical_comp)); my_address = itip_get_comp_attendee ( view->priv->registry, comp, NULL); g_object_unref (comp); comp = NULL; if (!prop) - prop = find_attendee (pitip->ical_comp, my_address); + prop = find_attendee (view->priv->ical_comp, my_address); if (!prop) - prop = find_attendee_if_sentby (pitip->ical_comp, my_address); + prop = find_attendee_if_sentby (view->priv->ical_comp, my_address); if (prop) { icalparameter *param; const gchar * delfrom; @@ -5713,14 +5509,14 @@ extract_itip_data (EMailPartItip *pitip, if ((param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER))) { delfrom = icalparameter_get_delegatedfrom (param); - pitip->delegator_address = g_strdup (itip_strip_mailto (delfrom)); + view->priv->delegator_address = g_strdup (itip_strip_mailto (delfrom)); } } g_free (my_address); prop = NULL; /* Determine any delegate sections */ - prop = icalcomponent_get_first_property (pitip->ical_comp, ICAL_X_PROPERTY); + prop = icalcomponent_get_first_property (view->priv->ical_comp, ICAL_X_PROPERTY); while (prop) { const gchar *x_name, *x_val; @@ -5728,19 +5524,19 @@ extract_itip_data (EMailPartItip *pitip, x_val = icalproperty_get_x (prop); if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-UID")) - pitip->calendar_uid = g_strdup (x_val); + view->priv->calendar_uid = g_strdup (x_val); else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-URI")) g_warning (G_STRLOC ": X-EVOLUTION-DELEGATOR-CALENDAR-URI used"); else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-ADDRESS")) - pitip->delegator_address = g_strdup (x_val); + view->priv->delegator_address = g_strdup (x_val); else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-NAME")) - pitip->delegator_name = g_strdup (x_val); + view->priv->delegator_name = g_strdup (x_val); - prop = icalcomponent_get_next_property (pitip->ical_comp, ICAL_X_PROPERTY); + prop = icalcomponent_get_next_property (view->priv->ical_comp, ICAL_X_PROPERTY); } /* Strip out procedural alarms for security purposes */ - alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT); + alarm_iter = icalcomponent_begin_component (view->priv->ical_comp, ICAL_VALARM_COMPONENT); while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) { icalproperty *p; @@ -5748,21 +5544,21 @@ extract_itip_data (EMailPartItip *pitip, p = icalcomponent_get_first_property (alarm_comp, ICAL_ACTION_PROPERTY); if (!p || icalproperty_get_action (p) == ICAL_ACTION_PROCEDURE) - icalcomponent_remove_component (pitip->ical_comp, alarm_comp); + icalcomponent_remove_component (view->priv->ical_comp, alarm_comp); icalcomponent_free (alarm_comp); } if (have_alarms) { - alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT); + alarm_iter = icalcomponent_begin_component (view->priv->ical_comp, ICAL_VALARM_COMPONENT); *have_alarms = icalcompiter_deref (&alarm_iter) != NULL; } } - pitip->comp = e_cal_component_new (); - if (!e_cal_component_set_icalcomponent (pitip->comp, pitip->ical_comp)) { - g_object_unref (pitip->comp); - pitip->comp = NULL; + view->priv->comp = e_cal_component_new (); + if (!e_cal_component_set_icalcomponent (view->priv->comp, view->priv->ical_comp)) { + g_object_unref (view->priv->comp); + view->priv->comp = NULL; set_itip_error ( view, @@ -5816,29 +5612,29 @@ extract_itip_data (EMailPartItip *pitip, } e_cal_component_alarm_set_trigger (acomp, trigger); - e_cal_component_add_alarm (pitip->comp, acomp); + e_cal_component_add_alarm (view->priv->comp, acomp); e_cal_component_alarm_free (acomp); } g_object_unref (settings); - find_from_address (view, pitip, pitip->ical_comp); - find_to_address (view, pitip, pitip->ical_comp, NULL); + find_from_address (view, view->priv->ical_comp); + find_to_address (view, view->priv->ical_comp, NULL); return TRUE; } static gboolean -idle_open_cb (gpointer data) +idle_open_cb (gpointer user_data) { - EMailPartItip *pitip = data; + ItipView *view = user_data; EShell *shell; const gchar *uris[2]; gchar *start, *end, *shell_uri; - start = isodate_from_time_t (pitip->start_time ? pitip->start_time : time (NULL)); - end = isodate_from_time_t (pitip->end_time ? pitip->end_time : time (NULL)); + start = isodate_from_time_t (view->priv->start_time ? view->priv->start_time : time (NULL)); + end = isodate_from_time_t (view->priv->end_time ? view->priv->end_time : time (NULL)); shell_uri = g_strdup_printf ("calendar:///?startdate=%s&enddate=%s", start, end); uris[0] = shell_uri; @@ -5857,100 +5653,99 @@ idle_open_cb (gpointer data) static void view_response_cb (ItipView *view, ItipViewResponse response, - gpointer data) + gpointer user_data) { - EMailPartItip *pitip = data; gboolean status = FALSE; icalproperty *prop; ECalComponentTransparency trans; if (response == ITIP_VIEW_RESPONSE_SAVE) { - save_vcalendar_cb (pitip); + save_vcalendar_cb (view); return; } - if (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) { + if (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST) { if (itip_view_get_free_time_check_state (view)) - e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT); + e_cal_component_set_transparency (view->priv->comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT); else - e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE); + e_cal_component_set_transparency (view->priv->comp, E_CAL_COMPONENT_TRANSP_OPAQUE); } else { - e_cal_component_get_transparency (pitip->comp, &trans); + e_cal_component_get_transparency (view->priv->comp, &trans); if (trans == E_CAL_COMPONENT_TRANSP_NONE) - e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE); + e_cal_component_set_transparency (view->priv->comp, E_CAL_COMPONENT_TRANSP_OPAQUE); } - if (!pitip->to_address && pitip->current_client != NULL) - e_client_get_backend_property_sync (E_CLIENT (pitip->current_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &pitip->to_address, NULL, NULL); + if (!view->priv->to_address && view->priv->current_client != NULL) + e_client_get_backend_property_sync (E_CLIENT (view->priv->current_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &view->priv->to_address, NULL, NULL); /* check if it is a recur instance (no master object) and * add a property */ if (itip_view_get_recur_check_state (view)) { prop = icalproperty_new_x ("All"); icalproperty_set_x_name (prop, "X-GW-RECUR-INSTANCES-MOD-TYPE"); - icalcomponent_add_property (pitip->ical_comp, prop); + icalcomponent_add_property (view->priv->ical_comp, prop); } switch (response) { case ITIP_VIEW_RESPONSE_ACCEPT: - if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) + if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) status = change_status ( view->priv->registry, - pitip->ical_comp, - pitip->to_address, + view->priv->ical_comp, + view->priv->to_address, ICAL_PARTSTAT_ACCEPTED); else status = TRUE; if (status) { - e_cal_component_rescan (pitip->comp); - update_item (pitip, view, response); + e_cal_component_rescan (view->priv->comp); + update_item (view, response); } break; case ITIP_VIEW_RESPONSE_TENTATIVE: status = change_status ( view->priv->registry, - pitip->ical_comp, - pitip->to_address, + view->priv->ical_comp, + view->priv->to_address, ICAL_PARTSTAT_TENTATIVE); if (status) { - e_cal_component_rescan (pitip->comp); - update_item (pitip, view, response); + e_cal_component_rescan (view->priv->comp); + update_item (view, response); } break; case ITIP_VIEW_RESPONSE_DECLINE: - if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) + if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) status = change_status ( view->priv->registry, - pitip->ical_comp, - pitip->to_address, + view->priv->ical_comp, + view->priv->to_address, ICAL_PARTSTAT_DECLINED); else { prop = icalproperty_new_x ("1"); icalproperty_set_x_name (prop, "X-GW-DECLINED"); - icalcomponent_add_property (pitip->ical_comp, prop); + icalcomponent_add_property (view->priv->ical_comp, prop); status = TRUE; } if (status) { - e_cal_component_rescan (pitip->comp); - update_item (pitip, view, response); + e_cal_component_rescan (view->priv->comp); + update_item (view, response); } break; case ITIP_VIEW_RESPONSE_UPDATE: - update_attendee_status (pitip, view); + update_attendee_status (view); break; case ITIP_VIEW_RESPONSE_CANCEL: - update_item (pitip, view, response); + update_item (view, response); break; case ITIP_VIEW_RESPONSE_REFRESH: - send_item (pitip, view); + send_item (view); break; case ITIP_VIEW_RESPONSE_OPEN: /* Prioritize ahead of GTK+ redraws. */ g_idle_add_full ( G_PRIORITY_HIGH_IDLE, - idle_open_cb, pitip, NULL); + idle_open_cb, g_object_ref (view), g_object_unref); return; default: break; @@ -6035,8 +5830,6 @@ in_proper_folder (CamelFolder *folder) void itip_view_init_view (ItipView *view) { - EShell *shell; - EClientCache *client_cache; ECalComponentText text; ECalComponentOrganizer organizer; ECalComponentDateTime datetime; @@ -6049,33 +5842,26 @@ itip_view_init_view (ItipView *view) const gchar *string, *org; gboolean response_enabled; gboolean have_alarms = FALSE; - EMailPartItip *info; - - info = view->priv->itip_part; - g_return_if_fail (info != NULL); - - shell = e_shell_get_default (); - client_cache = e_shell_get_client_cache (shell); - info->client_cache = g_object_ref (client_cache); + g_return_if_fail (ITIP_IS_VIEW (view)); /* Reset current client before initializing view */ - info->current_client = NULL; + view->priv->current_client = NULL; /* FIXME Handle multiple VEVENTS with the same UID, ie detached instances */ - if (!extract_itip_data (info, view, &have_alarms)) + if (!extract_itip_data (view, &have_alarms)) return; - response_enabled = in_proper_folder (info->folder); + response_enabled = in_proper_folder (view->priv->folder); if (!response_enabled) { itip_view_set_mode (view, ITIP_VIEW_MODE_HIDE_ALL); } else { itip_view_set_show_inherit_alarm_check ( view, - have_alarms && (info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST)); + have_alarms && (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST)); - switch (info->method) { + switch (view->priv->method) { case ICAL_METHOD_PUBLISH: case ICAL_METHOD_REQUEST: /* @@ -6087,7 +5873,7 @@ itip_view_init_view (ItipView *view) */ itip_view_set_mode ( view, - info->has_organizer ? + view->priv->has_organizer ? ITIP_VIEW_MODE_REQUEST : ITIP_VIEW_MODE_PUBLISH); break; @@ -6113,7 +5899,7 @@ itip_view_init_view (ItipView *view) /* Handle appointment requests from Microsoft Live. This is * a best-at-hand-now handling. Must be revisited when we have * better access to the source of such meetings */ - info->method = ICAL_METHOD_REQUEST; + view->priv->method = ICAL_METHOD_REQUEST; itip_view_set_mode (view, ITIP_VIEW_MODE_REQUEST); break; default: @@ -6121,13 +5907,13 @@ itip_view_init_view (ItipView *view) } } - itip_view_set_item_type (view, info->type); + itip_view_set_item_type (view, view->priv->type); if (response_enabled) { - switch (info->method) { + switch (view->priv->method) { case ICAL_METHOD_REQUEST: /* FIXME What about the name? */ - itip_view_set_delegator (view, info->delegator_name ? info->delegator_name : info->delegator_address); + itip_view_set_delegator (view, view->priv->delegator_name ? view->priv->delegator_name : view->priv->delegator_address); /* coverity[fallthrough] */ case ICAL_METHOD_PUBLISH: case ICAL_METHOD_ADD: @@ -6136,7 +5922,7 @@ itip_view_init_view (ItipView *view) itip_view_set_show_update_check (view, FALSE); /* An organizer sent this */ - e_cal_component_get_organizer (info->comp, &organizer); + e_cal_component_get_organizer (view->priv->comp, &organizer); org = organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value); itip_view_set_organizer (view, org); @@ -6144,11 +5930,11 @@ itip_view_init_view (ItipView *view) itip_view_set_organizer_sentby ( view, itip_strip_mailto (organizer.sentby)); - if (info->my_address) { - if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto (organizer.value), info->my_address)) - && !(organizer.sentby && !g_ascii_strcasecmp (itip_strip_mailto (organizer.sentby), info->my_address)) - && (info->to_address && g_ascii_strcasecmp (info->to_address, info->my_address))) - itip_view_set_proxy (view, info->to_name ? info->to_name : info->to_address); + if (view->priv->my_address) { + if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto (organizer.value), view->priv->my_address)) + && !(organizer.sentby && !g_ascii_strcasecmp (itip_strip_mailto (organizer.sentby), view->priv->my_address)) + && (view->priv->to_address && g_ascii_strcasecmp (view->priv->to_address, view->priv->my_address))) + itip_view_set_proxy (view, view->priv->to_name ? view->priv->to_name : view->priv->to_address); } break; case ICAL_METHOD_REPLY: @@ -6157,7 +5943,7 @@ itip_view_init_view (ItipView *view) itip_view_set_show_update_check (view, TRUE); /* An attendee sent this */ - e_cal_component_get_attendee_list (info->comp, &list); + e_cal_component_get_attendee_list (view->priv->comp, &list); if (list != NULL) { ECalComponentAttendee *attendee; @@ -6168,11 +5954,11 @@ itip_view_init_view (ItipView *view) if (attendee->sentby) itip_view_set_attendee_sentby (view, itip_strip_mailto (attendee->sentby)); - if (info->my_address) { - if (!(attendee->value && !g_ascii_strcasecmp (itip_strip_mailto (attendee->value), info->my_address)) - && !(attendee->sentby && !g_ascii_strcasecmp (itip_strip_mailto (attendee->sentby), info->my_address)) - && (info->from_address && g_ascii_strcasecmp (info->from_address, info->my_address))) - itip_view_set_proxy (view, info->from_name ? info->from_name : info->from_address); + if (view->priv->my_address) { + if (!(attendee->value && !g_ascii_strcasecmp (itip_strip_mailto (attendee->value), view->priv->my_address)) + && !(attendee->sentby && !g_ascii_strcasecmp (itip_strip_mailto (attendee->sentby), view->priv->my_address)) + && (view->priv->from_address && g_ascii_strcasecmp (view->priv->from_address, view->priv->my_address))) + itip_view_set_proxy (view, view->priv->from_name ? view->priv->from_name : view->priv->from_address); } e_cal_component_free_attendee_list (list); @@ -6184,15 +5970,15 @@ itip_view_init_view (ItipView *view) } } - e_cal_component_get_summary (info->comp, &text); + e_cal_component_get_summary (view->priv->comp, &text); itip_view_set_summary (view, text.value ? text.value : C_("cal-itip", "None")); - e_cal_component_get_location (info->comp, &string); + e_cal_component_get_location (view->priv->comp, &string); itip_view_set_location (view, string); /* Status really only applies for REPLY */ - if (response_enabled && info->method == ICAL_METHOD_REPLY) { - e_cal_component_get_attendee_list (info->comp, &list); + if (response_enabled && view->priv->method == ICAL_METHOD_REPLY) { + e_cal_component_get_attendee_list (view->priv->comp, &list); if (list != NULL) { ECalComponentAttendee *a = list->data; @@ -6216,12 +6002,12 @@ itip_view_init_view (ItipView *view) e_cal_component_free_attendee_list (list); } - if (info->method == ICAL_METHOD_REPLY - || info->method == ICAL_METHOD_COUNTER - || info->method == ICAL_METHOD_DECLINECOUNTER) { + if (view->priv->method == ICAL_METHOD_REPLY + || view->priv->method == ICAL_METHOD_COUNTER + || view->priv->method == ICAL_METHOD_DECLINECOUNTER) { /* FIXME Check spec to see if multiple comments are actually valid */ /* Comments for iTIP are limited to one per object */ - e_cal_component_get_comment_list (info->comp, &list); + e_cal_component_get_comment_list (view->priv->comp, &list); if (list) { ECalComponentText *text = list->data; @@ -6243,7 +6029,7 @@ itip_view_init_view (ItipView *view) e_cal_component_free_text_list (list); } - e_cal_component_get_description_list (info->comp, &list); + e_cal_component_get_description_list (view->priv->comp, &list); for (l = list; l; l = l->next) { ECalComponentText *text = l->data; @@ -6291,8 +6077,8 @@ itip_view_init_view (ItipView *view) g_object_unref (settings); - e_cal_component_get_dtstart (info->comp, &datetime); - info->start_time = 0; + e_cal_component_get_dtstart (view->priv->comp, &datetime); + view->priv->start_time = 0; if (datetime.value) { struct tm start_tm; @@ -6301,17 +6087,17 @@ itip_view_init_view (ItipView *view) if (datetime.value->is_utc) from_zone = icaltimezone_get_utc_timezone (); else if (!datetime.value->is_utc && datetime.tzid) - from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid); + from_zone = icalcomponent_get_timezone (view->priv->top_level, datetime.tzid); else from_zone = NULL; start_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone); itip_view_set_start (view, &start_tm, datetime.value->is_date); - info->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone); + view->priv->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone); } - icalcomp = e_cal_component_get_icalcomponent (info->comp); + icalcomp = e_cal_component_get_icalcomponent (view->priv->comp); /* Set the recurrence id */ if (check_is_instance (icalcomp) && datetime.value) { @@ -6322,13 +6108,13 @@ itip_view_init_view (ItipView *view) recur_id->type = E_CAL_COMPONENT_RANGE_SINGLE; recur_id->datetime.value = &icaltime; recur_id->datetime.tzid = icaltimezone_get_tzid (to_zone); - e_cal_component_set_recurid (info->comp, recur_id); + e_cal_component_set_recurid (view->priv->comp, recur_id); g_free (recur_id); /* it's ok to call g_free here */ } e_cal_component_free_datetime (&datetime); - e_cal_component_get_dtend (info->comp, &datetime); - info->end_time = 0; + e_cal_component_get_dtend (view->priv->comp, &datetime); + view->priv->end_time = 0; if (datetime.value) { struct tm end_tm; @@ -6337,7 +6123,7 @@ itip_view_init_view (ItipView *view) if (datetime.value->is_utc) from_zone = icaltimezone_get_utc_timezone (); else if (!datetime.value->is_utc && datetime.tzid) - from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid); + from_zone = icalcomponent_get_timezone (view->priv->top_level, datetime.tzid); else from_zone = NULL; @@ -6351,15 +6137,15 @@ itip_view_init_view (ItipView *view) end_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone); itip_view_set_end (view, &end_tm, datetime.value->is_date); - info->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone); + view->priv->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone); } e_cal_component_free_datetime (&datetime); /* Recurrence info */ /* FIXME Better recurring description */ - if (e_cal_component_has_recurrences (info->comp)) { + if (e_cal_component_has_recurrences (view->priv->comp)) { /* FIXME Tell the user we don't support recurring tasks */ - switch (info->type) { + switch (view->priv->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This meeting recurs")); break; @@ -6377,25 +6163,40 @@ itip_view_init_view (ItipView *view) g_signal_connect ( view, "response", - G_CALLBACK (view_response_cb), info); + G_CALLBACK (view_response_cb), NULL); if (response_enabled) { - itip_view_set_show_free_time_check (view, info->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS && (info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST)); + itip_view_set_show_free_time_check (view, view->priv->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS && (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST)); - if (info->calendar_uid) { - start_calendar_server_by_uid (info, view, info->calendar_uid, info->type); + if (view->priv->calendar_uid) { + start_calendar_server_by_uid (view, view->priv->calendar_uid, view->priv->type); } else { - find_server (info, view, info->comp); - set_buttons_sensitive (info, view); + find_server (view, view->priv->comp); + set_buttons_sensitive (view); } - } else if (view->priv->dom_document) { + } else if (view->priv->web_extension) { /* The Open Calendar button can be shown, thus enable it */ - WebKitDOMElement *el; - - el = webkit_dom_document_get_element_by_id ( - view->priv->dom_document, BUTTON_OPEN_CALENDAR); - webkit_dom_html_button_element_set_disabled ( - WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE); - g_object_unref (el); + enable_button (view, BUTTON_OPEN_CALENDAR, TRUE); } } + +void +itip_view_set_web_view (ItipView *view, + EWebView *web_view) +{ + g_return_if_fail (ITIP_IS_VIEW (view)); + if (web_view) + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + g_weak_ref_set (view->priv->web_view_weakref, web_view); + + itip_view_register_clicked_listener (view); +} + +EWebView * +itip_view_ref_web_view (ItipView *view) +{ + g_return_val_if_fail (ITIP_IS_VIEW (view), NULL); + + return g_weak_ref_get (view->priv->web_view_weakref); +} diff --git a/modules/itip-formatter/itip-view.h b/modules/itip-formatter/itip-view.h index 0ffd96f..7b220f8 100644 --- a/modules/itip-formatter/itip-view.h +++ b/modules/itip-formatter/itip-view.h @@ -27,7 +27,6 @@ #include <unistd.h> #include <gtk/gtk.h> -#include <webkit/webkitdom.h> #include <libecal/libecal.h> @@ -59,8 +58,6 @@ typedef struct _ItipView ItipView; typedef struct _ItipViewClass ItipViewClass; typedef struct _ItipViewPrivate ItipViewPrivate; -struct _EMailPartItip; - typedef enum { ITIP_VIEW_MODE_NONE, ITIP_VIEW_MODE_PUBLISH, @@ -109,17 +106,24 @@ struct _ItipViewClass { }; GType itip_view_get_type (void); -ItipView * itip_view_new (struct _EMailPartItip *puri, - EClientCache *client_cache); +ItipView * itip_view_new (guint64 page_id, + const gchar *part_id, + gpointer itip_part_ptr, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + CamelMimePart *itip_mime_part, + const gchar *vcalendar, + GCancellable *cancellable); void itip_view_init_view (ItipView *view); -void itip_view_write (EMailFormatter *formatter, +void itip_view_set_web_view (ItipView *view, + EWebView *web_view); +EWebView * itip_view_ref_web_view (ItipView *view); +void itip_view_write (gpointer itip_part, + EMailFormatter *formatter, GString *buffer); void itip_view_write_for_printing (ItipView *view, GString *buffer); -void itip_view_create_dom_bindings (ItipView *view, - WebKitDOMElement *element); -struct _EMailPartItip * - itip_view_get_mail_part (ItipView *view); EClientCache * itip_view_get_client_cache (ItipView *view); const gchar * itip_view_get_extension_name (ItipView *view); void itip_view_set_extension_name (ItipView *view, @@ -246,6 +250,8 @@ void itip_view_set_show_inherit_alarm_check void itip_view_set_error (ItipView *view, const gchar *error_html, gboolean show_save_btn); +GDBusProxy * itip_view_get_web_extension_proxy + (ItipView *view); G_END_DECLS diff --git a/modules/itip-formatter/web-extension/Makefile.am b/modules/itip-formatter/web-extension/Makefile.am new file mode 100644 index 0000000..a519744 --- /dev/null +++ b/modules/itip-formatter/web-extension/Makefile.am @@ -0,0 +1,26 @@ +webextensions_LTLIBRARIES = libmoduleitipformatterwebextension.la + +libmoduleitipformatterwebextension_la_SOURCES = \ + module-itip-formatter-web-extension.c \ + module-itip-formatter-web-extension.h \ + module-itip-formatter-dom-utils.c \ + module-itip-formatter-dom-utils.h + +libmoduleitipformatterwebextension_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(WEB_EXTENSIONS_CFLAGS) + +libmoduleitipformatterwebextension_la_LIBADD = \ + $(top_builddir)/e-util/libevolution-util.la \ + $(top_builddir)/web-extensions/libedomutils.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(WEB_EXTENSIONS_LIBS) + +libmoduleitipformatterwebextension_la_LDFLAGS = \ + -module -avoid-version -no-undefined + +-include $(top_srcdir)/git.mk diff --git a/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.c b/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.c new file mode 100644 index 0000000..72809ee --- /dev/null +++ b/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.c @@ -0,0 +1,642 @@ +/* + * module-itip-formatter-dom-utils.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#include "module-itip-formatter-dom-utils.h" + +#define WEBKIT_DOM_USE_UNSTABLE_API +#include <webkitdom/WebKitDOMHTMLElementUnstable.h> + +#include "module-itip-formatter-web-extension.h" +#include "../itip-view-elements-defines.h" + +#include <e-util/e-util.h> + +#define ITIP_WEB_EXTENSION_PAGE_ID_KEY "itip-web-extension-page-id" +#define ITIP_WEB_EXTENSION_PART_ID_KEY "itip-web-extension-part-id" + +static void +recur_toggled_cb (WebKitDOMHTMLInputElement *input, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + guint64 *ppage_id; + const gchar *part_id; + GError *error = NULL; + + ppage_id = g_object_get_data (G_OBJECT (input), ITIP_WEB_EXTENSION_PAGE_ID_KEY); + part_id = g_object_get_data (G_OBJECT (input), ITIP_WEB_EXTENSION_PART_ID_KEY); + if (!ppage_id || !part_id) { + g_warning ("%s: page_id/part_id not set on %p", G_STRFUNC, input); + return; + } + + g_dbus_connection_emit_signal ( + connection, + NULL, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE, + "RecurToggled", + g_variant_new ("(ts)", *ppage_id, part_id), + &error); + + if (error) { + g_warning ("Error emitting signal RecurToggled: %s\n", error->message); + g_error_free (error); + } +} + +static void +source_changed_cb (WebKitDOMElement *element, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + guint64 *ppage_id; + const gchar *part_id; + GError *error = NULL; + + ppage_id = g_object_get_data (G_OBJECT (element), ITIP_WEB_EXTENSION_PAGE_ID_KEY); + part_id = g_object_get_data (G_OBJECT (element), ITIP_WEB_EXTENSION_PART_ID_KEY); + if (!ppage_id || !part_id) { + g_warning ("%s: page_id/part_id not set on %p", G_STRFUNC, element); + return; + } + + g_dbus_connection_emit_signal ( + connection, + NULL, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE, + "SourceChanged", + g_variant_new ("(ts)", *ppage_id, part_id), + &error); + + if (error) { + g_warning ("Error emitting signal SourceChanged: %s\n", error->message); + g_error_free (error); + } +} + +static void +rsvp_toggled_cb (WebKitDOMHTMLInputElement *input, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + WebKitDOMElement *el; + WebKitDOMDocument *document; + gboolean rsvp; + + document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (input)); + rsvp = webkit_dom_html_input_element_get_checked (input); + el = webkit_dom_document_get_element_by_id ( + document, TEXTAREA_RSVP_COMMENT); + webkit_dom_html_text_area_element_set_disabled ( + WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp); +} + +/** + alarm_check_toggled_cb + check1 was changed, so make the second available based on state of the first check. +*/ +static void +alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + WebKitDOMDocument *document; + WebKitDOMElement *check2; + gchar *id; + + document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (check1)); +#if WEBKIT_CHECK_VERSION(2,2,0) /* XXX should really be (2,1,something) */ + id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (check1)); +#else + id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (check1)); +#endif + + if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) { + check2 = webkit_dom_document_get_element_by_id ( + document, CHECKBOX_KEEP_ALARM); + } else { + check2 = webkit_dom_document_get_element_by_id ( + document, CHECKBOX_INHERIT_ALARM); + } + + g_free (id); + + webkit_dom_html_input_element_set_disabled ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (check2), + (webkit_dom_html_element_get_hidden ( + WEBKIT_DOM_HTML_ELEMENT (check1)) && + webkit_dom_html_input_element_get_checked (check1))); +} + +void +module_itip_formatter_dom_utils_create_dom_bindings (WebKitDOMDocument *document, + guint64 page_id, + const gchar *part_id, + GDBusConnection *connection) +{ + WebKitDOMElement *el; + + g_return_if_fail (part_id && *part_id); + + el = webkit_dom_document_get_element_by_id (document, CHECKBOX_RECUR); + if (el) { + guint64 *ppage_id; + + ppage_id = g_new0 (guint64, 1); + *ppage_id = page_id; + + g_object_set_data_full (G_OBJECT (el), ITIP_WEB_EXTENSION_PAGE_ID_KEY, ppage_id, g_free); + g_object_set_data_full (G_OBJECT (el), ITIP_WEB_EXTENSION_PART_ID_KEY, g_strdup (part_id), g_free); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (el), "click", + G_CALLBACK (recur_toggled_cb), FALSE, connection); + } + + el = webkit_dom_document_get_element_by_id (document, SELECT_ESOURCE); + if (el) { + guint64 *ppage_id; + + ppage_id = g_new0 (guint64, 1); + *ppage_id = page_id; + + g_object_set_data_full (G_OBJECT (el), ITIP_WEB_EXTENSION_PAGE_ID_KEY, ppage_id, g_free); + g_object_set_data_full (G_OBJECT (el), ITIP_WEB_EXTENSION_PART_ID_KEY, g_strdup (part_id), g_free); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (el), "change", + G_CALLBACK (source_changed_cb), FALSE, connection); + } + + el = webkit_dom_document_get_element_by_id (document, CHECKBOX_RSVP); + if (el) { + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (el), "click", + G_CALLBACK (rsvp_toggled_cb), FALSE, connection); + } + + el = webkit_dom_document_get_element_by_id (document, CHECKBOX_INHERIT_ALARM); + if (el) { + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (el), "click", + G_CALLBACK (alarm_check_toggled_cb), FALSE, connection); + } + + el = webkit_dom_document_get_element_by_id (document, CHECKBOX_KEEP_ALARM); + if (el) { + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (el), "click", + G_CALLBACK (alarm_check_toggled_cb), FALSE, connection); + } +} + +void +module_itip_formatter_dom_utils_show_button (WebKitDOMDocument *document, + const gchar *button_id) +{ + WebKitDOMElement *button; + + button = webkit_dom_document_get_element_by_id (document, button_id); + webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (button), FALSE); +} + +void +module_itip_formatter_dom_utils_enable_button (WebKitDOMDocument *document, + const gchar *button_id, + gboolean enable) +{ + WebKitDOMElement *el; + + el = webkit_dom_document_get_element_by_id (document, button_id); + webkit_dom_html_button_element_set_disabled ( + WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), !enable); +} + +gboolean +module_itip_formatter_dom_utils_input_is_checked (WebKitDOMDocument *document, + const gchar *input_id) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_get_element_by_id (document, input_id); + + if (!element) + return FALSE; + + return webkit_dom_html_input_element_get_checked ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (element)); +} + +void +module_itip_formatter_dom_utils_input_set_checked (WebKitDOMDocument *document, + const gchar *input_id, + gboolean checked) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_get_element_by_id (document, input_id); + + if (!element) + return; + + webkit_dom_html_input_element_set_checked ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (element), checked); +} + +void +module_itip_formatter_dom_utils_show_checkbox (WebKitDOMDocument *document, + const gchar *id, + gboolean show, + gboolean update_second) +{ + WebKitDOMElement *label; + WebKitDOMElement *el; + gchar *row_id; + + el = webkit_dom_document_get_element_by_id (document, id); + webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); + + label = webkit_dom_element_get_next_element_sibling (el); + webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show); + + if (!show) { + webkit_dom_html_input_element_set_checked ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE); + } + + if (update_second) { + /* and update state of the second check */ + alarm_check_toggled_cb ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (el), + NULL, NULL); + } + + row_id = g_strconcat ("table_row_", id, NULL); + el = webkit_dom_document_get_element_by_id (document, row_id); + webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show); + g_free (row_id); +} + +void +module_itip_formatter_dom_utils_set_buttons_sensitive (WebKitDOMDocument *document, + gboolean sensitive) +{ + WebKitDOMElement *el, *cell; + + el = webkit_dom_document_get_element_by_id ( + document, CHECKBOX_UPDATE); + webkit_dom_html_input_element_set_disabled ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); + + el = webkit_dom_document_get_element_by_id ( + document, CHECKBOX_RECUR); + webkit_dom_html_input_element_set_disabled ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); + + el = webkit_dom_document_get_element_by_id ( + document, CHECKBOX_FREE_TIME); + webkit_dom_html_input_element_set_disabled ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); + + el = webkit_dom_document_get_element_by_id ( + document, CHECKBOX_KEEP_ALARM); + webkit_dom_html_input_element_set_disabled ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); + + el = webkit_dom_document_get_element_by_id ( + document, CHECKBOX_INHERIT_ALARM); + webkit_dom_html_input_element_set_disabled ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); + + el = webkit_dom_document_get_element_by_id ( + document, CHECKBOX_RSVP); + webkit_dom_html_input_element_set_disabled ( + WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive); + + el = webkit_dom_document_get_element_by_id ( + document, TEXTAREA_RSVP_COMMENT); + webkit_dom_html_text_area_element_set_disabled ( + WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive); + + el = webkit_dom_document_get_element_by_id ( + document, TABLE_ROW_BUTTONS); + cell = webkit_dom_element_get_first_element_child (el); + do { + WebKitDOMElement *btn; + btn = webkit_dom_element_get_first_element_child (cell); + if (!webkit_dom_html_element_get_hidden ( + WEBKIT_DOM_HTML_ELEMENT (btn))) { + webkit_dom_html_button_element_set_disabled ( + WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive); + } + } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL); +} + +void +module_itip_formatter_dom_utils_set_area_text (WebKitDOMDocument *document, + const gchar *area_id, + const gchar *text) +{ + WebKitDOMElement *row, *col; + + row = webkit_dom_document_get_element_by_id (document, area_id); + webkit_dom_html_element_set_hidden ( + WEBKIT_DOM_HTML_ELEMENT (row), (g_strcmp0 (text, "") == 0)); + + col = webkit_dom_element_get_last_element_child (row); + webkit_dom_element_set_inner_html (col, text, NULL); +} + +void +module_itip_formatter_dom_utils_element_set_access_key (WebKitDOMDocument *document, + const gchar *element_id, + const gchar *access_key) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_get_element_by_id (document, element_id); + + if (!element) + return; + + webkit_dom_html_element_set_access_key ( + WEBKIT_DOM_HTML_ELEMENT (element), access_key); +} + +void +module_itip_formatter_dom_utils_element_hide_child_nodes (WebKitDOMDocument *document, + const gchar *element_id) +{ + WebKitDOMElement *element, *cell, *button; + + element = webkit_dom_document_get_element_by_id (document, element_id); + + if (!element) + return; + + element = webkit_dom_document_get_element_by_id (document, element_id); + cell = webkit_dom_element_get_first_element_child (element); + do { + button = webkit_dom_element_get_first_element_child (cell); + webkit_dom_html_element_set_hidden ( + WEBKIT_DOM_HTML_ELEMENT (button), TRUE); + } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL); +} + +void +module_itip_formatter_dom_utils_enable_select (WebKitDOMDocument *document, + const gchar *select_id, + gboolean enabled) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_get_element_by_id (document, select_id); + + if (!element) + return; + + webkit_dom_html_select_element_set_disabled ( + WEBKIT_DOM_HTML_SELECT_ELEMENT (element), !enabled); +} + +gboolean +module_itip_formatter_dom_utils_select_is_enabled (WebKitDOMDocument *document, + const gchar *select_id) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_get_element_by_id (document, select_id); + + if (!element) + return FALSE; + + return !webkit_dom_html_select_element_get_disabled ( + WEBKIT_DOM_HTML_SELECT_ELEMENT (element)); +} + +gchar * +module_itip_formatter_dom_utils_select_get_value (WebKitDOMDocument *document, + const gchar *select_id) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_get_element_by_id (document, select_id); + + if (!element) + return NULL; + + return webkit_dom_html_select_element_get_value ( + WEBKIT_DOM_HTML_SELECT_ELEMENT (element)); +} + +void +module_itip_formatter_dom_utils_select_set_selected (WebKitDOMDocument *document, + const gchar *select_id, + const gchar *option) +{ + WebKitDOMElement *element; + gint length, ii; + + element = webkit_dom_document_get_element_by_id (document, select_id); + + if (!element) + return; + + length = webkit_dom_html_select_element_get_length ( + WEBKIT_DOM_HTML_SELECT_ELEMENT (element)); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + WebKitDOMHTMLOptionElement *option_element; + gchar *value; + + node = webkit_dom_html_select_element_item ( + WEBKIT_DOM_HTML_SELECT_ELEMENT (element), ii); + option_element = WEBKIT_DOM_HTML_OPTION_ELEMENT (node); + + value = webkit_dom_html_option_element_get_value (option_element); + if (g_strcmp0 (value, option) == 0) { + webkit_dom_html_option_element_set_selected ( + option_element, TRUE); + + g_free (value); + break; + } + + g_free (value); + } +} + +void +module_itip_formatter_dom_utils_update_times (WebKitDOMDocument *document, + const gchar *element_id, + const gchar *header, + const gchar *label) +{ + WebKitDOMElement *element, *col; + + element = webkit_dom_document_get_element_by_id (document, element_id); + + if (!element) + return; + + webkit_dom_html_element_set_hidden ( + WEBKIT_DOM_HTML_ELEMENT (element), FALSE); + + col = webkit_dom_element_get_first_element_child (element); + webkit_dom_element_set_inner_html (col, header, NULL); + + col = webkit_dom_element_get_last_element_child (element); + webkit_dom_element_set_inner_html (col, label, NULL); +} + +void +module_itip_formatter_dom_utils_append_info_item_row (WebKitDOMDocument *document, + const gchar *table_id, + const gchar *row_id, + const gchar *icon_name, + const gchar *message) +{ + WebKitDOMElement *table; + WebKitDOMHTMLElement *cell, *row; + + table = webkit_dom_document_get_element_by_id (document, table_id); + + if (!table) + return; + + table = webkit_dom_document_get_element_by_id (document, table_id); + row = webkit_dom_html_table_element_insert_row ( + WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL); + + webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (row), row_id); + + cell = webkit_dom_html_table_row_element_insert_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL); + + if (icon_name) { + WebKitDOMElement *image; + gchar *icon_uri; + + image = webkit_dom_document_create_element ( + document, "IMG", NULL); + + icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name); + webkit_dom_html_image_element_set_src ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri); + g_free (icon_uri); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (cell), + WEBKIT_DOM_NODE (image), + NULL); + } + + cell = webkit_dom_html_table_row_element_insert_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL); + + webkit_dom_element_set_inner_html (WEBKIT_DOM_ELEMENT (cell), message, NULL); +} + +void +module_itip_formatter_dom_utils_enable_text_area (WebKitDOMDocument *document, + const gchar *area_id, + gboolean enable) +{ + WebKitDOMElement *el; + + el = webkit_dom_document_get_element_by_id (document, area_id); + webkit_dom_html_text_area_element_set_disabled ( + WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !enable); +} + +void +module_itip_formatter_dom_utils_text_area_set_value (WebKitDOMDocument *document, + const gchar *area_id, + const gchar *value) +{ + WebKitDOMElement *el; + + el = webkit_dom_document_get_element_by_id (document, area_id); + webkit_dom_html_text_area_element_set_value ( + WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), value); +} + +gchar * +module_itip_formatter_dom_utils_text_area_get_value (WebKitDOMDocument *document, + const gchar *area_id) +{ + WebKitDOMElement *el; + + el = webkit_dom_document_get_element_by_id (document, area_id); + return webkit_dom_html_text_area_element_get_value ( + WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el)); +} + +void +module_itip_formatter_dom_utils_rebuild_source_list (WebKitDOMDocument *document, + const gchar *optgroup_id, + const gchar *optgroup_label, + const gchar *option_id, + const gchar *option_label, + gboolean writable) +{ + WebKitDOMElement *option; + WebKitDOMElement *select; + WebKitDOMHTMLOptGroupElement *optgroup; + + select = webkit_dom_document_get_element_by_id (document, SELECT_ESOURCE); + + if (!select) + return; + + optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT ( + webkit_dom_document_get_element_by_id ( + document, optgroup_id)); + + if (!optgroup) { + optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT ( + webkit_dom_document_create_element ( + document, "OPTGROUP", NULL)); + webkit_dom_html_opt_group_element_set_label ( + optgroup, optgroup_label); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (select), WEBKIT_DOM_NODE (optgroup), NULL); + } + + option = webkit_dom_document_create_element (document, "OPTION", NULL); + webkit_dom_html_option_element_set_value ( + WEBKIT_DOM_HTML_OPTION_ELEMENT (option), option_id); + webkit_dom_html_option_element_set_label ( + WEBKIT_DOM_HTML_OPTION_ELEMENT (option), option_label); + webkit_dom_element_set_inner_html (option, option_label, NULL); + + webkit_dom_element_set_class_name ( + WEBKIT_DOM_ELEMENT (option), "calendar"); + + if (!writable) { + webkit_dom_html_option_element_set_disabled ( + WEBKIT_DOM_HTML_OPTION_ELEMENT (option), TRUE); + } + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (optgroup), + WEBKIT_DOM_NODE (option), + NULL); +} diff --git a/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.h b/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.h new file mode 100644 index 0000000..8fab80f --- /dev/null +++ b/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.h @@ -0,0 +1,111 @@ +/* + * module-itip-formatter-dom-utils.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef MODULE_ITIP_FORMATTER_DOM_UTILS_H +#define MODULE_ITIP_FORMATTER_DOM_UTILS_H + +#include <webkitdom/webkitdom.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +void module_itip_formatter_dom_utils_create_dom_bindings + (WebKitDOMDocument *document, + guint64 page_id, + const gchar *part_id, + GDBusConnection *connection); +void module_itip_formatter_dom_utils_show_button + (WebKitDOMDocument *document, + const gchar *button_id); +void module_itip_formatter_dom_utils_enable_button + (WebKitDOMDocument *document, + const gchar *button_id, + gboolean enable); +void module_itip_formatter_dom_utils_input_set_checked + (WebKitDOMDocument *document, + const gchar *input_id, + gboolean checked); +gboolean module_itip_formatter_dom_utils_input_is_checked + (WebKitDOMDocument *document, + const gchar *input_id); +void module_itip_formatter_dom_utils_show_checkbox + (WebKitDOMDocument *document, + const gchar *id, + gboolean show, + gboolean update_second); +void module_itip_formatter_dom_utils_set_buttons_sensitive + (WebKitDOMDocument *document, + gboolean sensitive); +void module_itip_formatter_dom_utils_set_area_text + (WebKitDOMDocument *document, + const gchar *area_id, + const gchar *text); +void module_itip_formatter_dom_utils_element_set_access_key + (WebKitDOMDocument *document, + const gchar *element_id, + const gchar *access_key); +void module_itip_formatter_dom_utils_element_hide_child_nodes + (WebKitDOMDocument *document, + const gchar *element_id); +void module_itip_formatter_dom_utils_enable_select + (WebKitDOMDocument *document, + const gchar *select_id, + gboolean enabled); +gboolean module_itip_formatter_dom_utils_select_is_enabled + (WebKitDOMDocument *document, + const gchar *select_id); +gchar * module_itip_formatter_dom_utils_select_get_value + (WebKitDOMDocument *document, + const gchar *select_id); +void module_itip_formatter_dom_utils_select_set_selected + (WebKitDOMDocument *document, + const gchar *select_id, + const gchar *option); +void module_itip_formatter_dom_utils_update_times + (WebKitDOMDocument *document, + const gchar *element_id, + const gchar *header, + const gchar *label); +void module_itip_formatter_dom_utils_append_info_item_row + (WebKitDOMDocument *document, + const gchar *table_id, + const gchar *row_id, + const gchar *icon_name, + const gchar *message); +void module_itip_formatter_dom_utils_enable_text_area + (WebKitDOMDocument *document, + const gchar *area_id, + gboolean enable); +void module_itip_formatter_dom_utils_text_area_set_value + (WebKitDOMDocument *document, + const gchar *area_id, + const gchar *value); +gchar * module_itip_formatter_dom_utils_text_area_get_value + (WebKitDOMDocument *document, + const gchar *area_id); +void module_itip_formatter_dom_utils_rebuild_source_list + (WebKitDOMDocument *document, + const gchar *optgroup_id, + const gchar *optgroup_label, + const gchar *option_id, + const gchar *option_label, + gboolean writable); +G_END_DECLS + +#endif /* MODULE_ITIP_FORMATTER_DOM_UTILS_H */ diff --git a/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.c b/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.c new file mode 100644 index 0000000..b83e590 --- /dev/null +++ b/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.c @@ -0,0 +1,646 @@ +/* + * module-itip-formatter-web-extension.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#include "module-itip-formatter-web-extension.h" + +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <webkit2/webkit-web-extension.h> + +#include <web-extensions/e-dom-utils.h> + +#include "module-itip-formatter-dom-utils.h" + +/* FIXME Clean it */ +static GDBusConnection *dbus_connection; + +static const char introspection_xml[] = +"<node>" +" <interface name='" MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE "'>" +" <signal name='RecurToggled'>" +" <arg type='t' name='page_id' direction='out'/>" +" <arg type='s' name='part_id' direction='out'/>" +" </signal>" +" <signal name='SourceChanged'>" +" <arg type='t' name='page_id' direction='out'/>" +" <arg type='s' name='part_id' direction='out'/>" +" </signal>" +" <method name='CreateDOMBindings'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" </method>" +" <method name='ShowButton'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='button_id' direction='in'/>" +" </method>" +" <method name='ElementSetInnerHTML'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='inner_html' direction='in'/>" +" </method>" +" <method name='RemoveElement'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" </method>" +" <method name='ElementRemoveChildNodes'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" </method>" +" <method name='EnableButton'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='button_id' direction='in'/>" +" <arg type='b' name='enable' direction='in'/>" +" </method>" +" <method name='ElementIsHidden'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='b' name='is_hidden' direction='out'/>" +" </method>" +" <method name='HideElement'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='b' name='hide' direction='in'/>" +" </method>" +" <method name='InputSetChecked'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='input_id' direction='in'/>" +" <arg type='b' name='checked' direction='in'/>" +" </method>" +" <method name='InputIsChecked'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='input_id' direction='in'/>" +" <arg type='b' name='checked' direction='out'/>" +" </method>" +" <method name='ShowCheckbox'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='id' direction='in'/>" +" <arg type='b' name='show' direction='in'/>" +" <arg type='b' name='update_second' direction='in'/>" +" </method>" +" <method name='SetButtonsSensitive'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='b' name='sensitive' direction='in'/>" +" </method>" +" <method name='SetAreaText'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='id' direction='in'/>" +" <arg type='s' name='text' direction='in'/>" +" </method>" +" <method name='ElementSetAccessKey'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='access_key' direction='in'/>" +" </method>" +" <method name='ElementHideChildNodes'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" </method>" +" <method name='EnableSelect'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='select_id' direction='in'/>" +" <arg type='b' name='enable' direction='in'/>" +" </method>" +" <method name='SelectIsEnabled'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='select_id' direction='in'/>" +" <arg type='b' name='enable' direction='out'/>" +" </method>" +" <method name='SelectGetValue'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='select_id' direction='in'/>" +" <arg type='s' name='value' direction='out'/>" +" </method>" +" <method name='SelectSetSelected'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='select_id' direction='in'/>" +" <arg type='s' name='option' direction='in'/>" +" </method>" +" <method name='UpdateTimes'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='header' direction='in'/>" +" <arg type='s' name='label' direction='in'/>" +" </method>" +" <method name='AppendInfoItemRow'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='table_id' direction='in'/>" +" <arg type='s' name='row_id' direction='in'/>" +" <arg type='s' name='icon_name' direction='in'/>" +" <arg type='s' name='message' direction='in'/>" +" </method>" +" <method name='EnableTextArea'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='area_id' direction='in'/>" +" <arg type='b' name='enable' direction='in'/>" +" </method>" +" <method name='TextAreaSetValue'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='area_id' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" </method>" +" <method name='TextAreaGetValue'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='area_id' direction='in'/>" +" <arg type='s' name='value' direction='out'/>" +" </method>" +" <method name='RebuildSourceList'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='part_id' direction='in'/>" +" <arg type='s' name='optgroup_id' direction='in'/>" +" <arg type='s' name='optgroup_label' direction='in'/>" +" <arg type='s' name='option_id' direction='in'/>" +" <arg type='s' name='option_label' direction='in'/>" +" <arg type='b' name='writable' direction='in'/>" +" </method>" +" </interface>" +"</node>"; + +static WebKitDOMDocument * +get_webkit_document_or_return_dbus_error (GDBusMethodInvocation *invocation, + WebKitWebExtension *web_extension, + guint64 page_id) +{ + WebKitDOMDocument *document; + WebKitWebPage *web_page; + + web_page = webkit_web_extension_get_page (web_extension, page_id); + if (!web_page) { + g_dbus_method_invocation_return_error ( + invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Invalid page ID: %" G_GUINT64_FORMAT, page_id); + return NULL; + } + + document = webkit_web_page_get_dom_document (web_page); + if (!document) { + g_dbus_method_invocation_return_error ( + invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "No document for page ID: %" G_GUINT64_FORMAT, page_id); + return NULL; + } + + return document; +} + +static WebKitDOMDocument * +find_webkit_document_for_partid_or_return_dbus_error (GDBusMethodInvocation *invocation, + WebKitDOMDocument *owner, + const gchar *part_id) +{ + WebKitDOMElement *element; + + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + g_return_val_if_fail (WEBKIT_DOM_IS_DOCUMENT (owner), NULL); + g_return_val_if_fail (part_id && *part_id, NULL); + + element = e_dom_utils_find_element_by_id (owner, part_id); + if (element && WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) { + WebKitDOMDocument *document = webkit_dom_html_iframe_element_get_content_document (WEBKIT_DOM_HTML_IFRAME_ELEMENT (element)); + return document; + } + + if (element) + g_dbus_method_invocation_return_error ( + invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Part ID '%s' is not IFRAME, but %s", part_id, G_OBJECT_TYPE_NAME (element)); + else + g_dbus_method_invocation_return_error ( + invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Part ID '%s' not found", part_id); + return NULL; +} + +static void +handle_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + WebKitWebExtension *web_extension = WEBKIT_WEB_EXTENSION (user_data); + WebKitDOMDocument *document; + const gchar *part_id = NULL; + guint64 page_id; + + if (g_strcmp0 (interface_name, MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE) != 0) + return; + + if (g_strcmp0 (method_name, "CreateDOMBindings") == 0) { + g_variant_get (parameters, "(t&s)", &page_id, &part_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_create_dom_bindings (document, page_id, part_id, connection); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "ShowButton") == 0) { + const gchar *button_id; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &button_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_show_button (document, button_id); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "EnableButton") == 0) { + const gchar *button_id; + gboolean enable; + + g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &button_id, &enable); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_enable_button (document, button_id, enable); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "ElementSetInnerHTML") == 0) { + const gchar *element_id, *inner_html; + + g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &element_id, &inner_html); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + e_dom_utils_element_set_inner_html (document, element_id, inner_html); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "RemoveElement") == 0) { + const gchar *element_id; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &element_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + e_dom_utils_remove_element (document, element_id); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "ElementRemoveChildNodes") == 0) { + const gchar *element_id; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &element_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + e_dom_utils_element_remove_child_nodes (document, element_id); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "HideElement") == 0) { + const gchar *element_id; + gboolean hide; + + g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &element_id, &hide); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + e_dom_utils_hide_element (document, element_id, hide); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "ElementIsHidden") == 0) { + const gchar *element_id; + gboolean hidden; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &element_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + hidden = e_dom_utils_element_is_hidden (document, element_id); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", hidden)); + } + } else if (g_strcmp0 (method_name, "InputSetChecked") == 0) { + const gchar *input_id; + gboolean checked; + + g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &input_id, &checked); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_input_set_checked (document, input_id, checked); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "InputIsChecked") == 0) { + const gchar *input_id; + gboolean checked; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &input_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + checked = module_itip_formatter_dom_utils_input_is_checked (document, input_id); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", checked)); + } + } else if (g_strcmp0 (method_name, "ShowCheckbox") == 0) { + const gchar *id; + gboolean show, update_second; + + g_variant_get (parameters, "(t&s&sbb)", &page_id, &part_id, &id, &show, &update_second); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_show_checkbox (document, id, show, update_second); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "SetButtonsSensitive") == 0) { + gboolean sensitive; + + g_variant_get (parameters, "(t&sb)", &page_id, &part_id, &sensitive); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_set_buttons_sensitive (document, sensitive); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "SetAreaText") == 0) { + const gchar *id, *text; + + g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &id, &text); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_set_area_text (document, id, text); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "ElementSetAccessKey") == 0) { + const gchar *element_id, *access_key; + + g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &element_id, &access_key); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_element_set_access_key (document, element_id, access_key); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "ElementHideChildNodes") == 0) { + const gchar *element_id; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &element_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_element_hide_child_nodes (document, element_id); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "EnableSelect") == 0) { + const gchar *select_id; + gboolean enable; + + g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &select_id, &enable); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_enable_select (document, select_id, enable); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "SelectIsEnabled") == 0) { + const gchar *select_id; + gboolean enabled; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &select_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + enabled = module_itip_formatter_dom_utils_select_is_enabled (document, select_id); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", enabled)); + } + } else if (g_strcmp0 (method_name, "SelectGetValue") == 0) { + const gchar *select_id; + gchar *value; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &select_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + value = module_itip_formatter_dom_utils_select_get_value (document, select_id); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string (value ? value : g_strdup ("")))); + } + } else if (g_strcmp0 (method_name, "SelectSetSelected") == 0) { + const gchar *select_id, *option; + + g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &select_id, &option); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_select_set_selected (document, select_id, option); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "UpdateTimes") == 0) { + const gchar *element_id, *header, *label; + + g_variant_get (parameters, "(t&s&s&s&s)", &page_id, &part_id, &element_id, &header, &label); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_update_times (document, element_id, header, label); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "AppendInfoItemRow") == 0) { + const gchar *table_id, *row_id, *icon_name, *message; + + g_variant_get (parameters, "(t&s&s&s&s&s)", &page_id, &part_id, &table_id, &row_id, &icon_name, &message); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_append_info_item_row (document, table_id, row_id, icon_name, message); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "EnableTextArea") == 0) { + const gchar *area_id; + gboolean enable; + + g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &area_id, &enable); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_enable_text_area (document, area_id, enable); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "TextAreaSetValue") == 0) { + const gchar *area_id, *value; + + g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &area_id, &value); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_text_area_set_value (document, area_id, value); + g_dbus_method_invocation_return_value (invocation, NULL); + } + } else if (g_strcmp0 (method_name, "TextAreaGetValue") == 0) { + const gchar *area_id; + gchar *value; + + g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &area_id); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + value = module_itip_formatter_dom_utils_text_area_get_value (document, area_id); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string (value ? value : g_strdup ("")))); + } + } else if (g_strcmp0 (method_name, "RebuildSourceList") == 0) { + const gchar *optgroup_id, *optgroup_label, *option_id, *option_label; + gboolean writable; + + g_variant_get (parameters,"(t&s&s&s&s&sb)", &page_id, &part_id, &optgroup_id, &optgroup_label, &option_id, &option_label, &writable); + + document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id); + if (document) + document = find_webkit_document_for_partid_or_return_dbus_error (invocation, document, part_id); + if (document) { + module_itip_formatter_dom_utils_rebuild_source_list ( + document, + optgroup_id, + optgroup_label, + option_id, + option_label, + writable); + + g_dbus_method_invocation_return_value (invocation, NULL); + } + } +} + +static const GDBusInterfaceVTable interface_vtable = { + handle_method_call, + NULL, + NULL +}; + +static void +bus_acquired_cb (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + guint registration_id; + GError *error = NULL; + static GDBusNodeInfo *introspection_data = NULL; + + if (!introspection_data) + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + + registration_id = + g_dbus_connection_register_object ( + connection, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH, + introspection_data->interfaces[0], + &interface_vtable, + g_object_ref (user_data), + g_object_unref, + &error); + + if (!registration_id) { + g_warning ("Failed to register object: %s\n", error->message); + g_error_free (error); + } else { + dbus_connection = connection; + g_object_add_weak_pointer (G_OBJECT (connection), (gpointer *)&dbus_connection); + } +} + +/* Forward declaration */ +G_MODULE_EXPORT void webkit_web_extension_initialize (WebKitWebExtension *extension); + +G_MODULE_EXPORT void +webkit_web_extension_initialize (WebKitWebExtension *extension) +{ + g_bus_own_name ( + G_BUS_TYPE_SESSION, + MODULE_ITIP_FORMATTER_WEB_EXTENSION_SERVICE_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_cb, + NULL, NULL, + g_object_ref (extension), + g_object_unref); +} diff --git a/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.h b/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.h new file mode 100644 index 0000000..b9ffae0 --- /dev/null +++ b/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.h @@ -0,0 +1,26 @@ +/* + * module-itip-formatter-web-extension.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef MODULE_ITIP_FORMATTER_WEB_EXTENSION_H +#define MODULE_ITIP_FORMATTER_WEB_EXTENSION_H + +#define MODULE_ITIP_FORMATTER_WEB_EXTENSION_SERVICE_NAME "org.gnome.Evolution.Module.ItipFormatter.WebExtension" +#define MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH "/org/gnome/Evolution/Module/ItipFormatter/WebExtension" +#define MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE "org.gnome.Evolution.Module.ItipFormatter.WebExtension" + +#endif /* MODULE_ITIP_FORMATTER_WEB_EXTENSION_H */ diff --git a/modules/mail/e-mail-attachment-handler.c b/modules/mail/e-mail-attachment-handler.c index 50d0c74..a5252b8 100644 --- a/modules/mail/e-mail-attachment-handler.c +++ b/modules/mail/e-mail-attachment-handler.c @@ -167,6 +167,65 @@ exit: return message; } +typedef struct _CreateComposerData { + CamelMimeMessage *message; + CamelFolder *folder; + gboolean is_redirect; + + gboolean is_reply; + EMailReplyType reply_type; + + gboolean is_forward; + EMailForwardStyle forward_style; +} CreateComposerData; + +static void +create_composer_data_free (CreateComposerData *ccd) +{ + if (ccd) { + g_clear_object (&ccd->message); + g_clear_object (&ccd->folder); + g_free (ccd); + } +} + +static void +mail_attachment_handler_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CreateComposerData *ccd = user_data; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (ccd != NULL); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + if (ccd->is_redirect) { + em_utils_redirect_message (composer, ccd->message); + } else if (ccd->is_reply) { + GSettings *settings; + EMailReplyStyle style; + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + style = g_settings_get_enum (settings, "reply-style-name"); + g_object_unref (settings); + + em_utils_reply_to_message (composer, ccd->message, NULL, NULL, ccd->reply_type, style, NULL, NULL); + } else if (ccd->is_forward) { + em_utils_forward_message (composer, ccd->message, ccd->forward_style, ccd->folder, NULL); + } else { + em_utils_edit_message (composer, ccd->folder, ccd->message, NULL, TRUE); + } + } + + create_composer_data_free (ccd); +} + static void mail_attachment_handler_forward_with_style (EAttachmentHandler *handler, EMailForwardStyle style) @@ -174,6 +233,8 @@ mail_attachment_handler_forward_with_style (EAttachmentHandler *handler, EMailAttachmentHandlerPrivate *priv; CamelMimeMessage *message; CamelFolder *folder; + CreateComposerData *ccd; + EShell *shell; priv = E_MAIL_ATTACHMENT_HANDLER_GET_PRIVATE (handler); @@ -181,11 +242,15 @@ mail_attachment_handler_forward_with_style (EAttachmentHandler *handler, g_return_if_fail (message != NULL); folder = mail_attachment_handler_guess_folder_ref (handler); + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (priv->backend)); - em_utils_forward_message (priv->backend, message, style, folder, NULL); + ccd = g_new0 (CreateComposerData, 1); + ccd->message = message; + ccd->folder = folder; + ccd->is_forward = TRUE; + ccd->forward_style = style; - g_clear_object (&folder); - g_object_unref (message); + e_msg_composer_new (shell, mail_attachment_handler_composer_created_cb, ccd); } static void @@ -207,9 +272,8 @@ mail_attachment_handler_reply (EAttachmentHandler *handler, EMailReplyType reply_type) { EMailAttachmentHandlerPrivate *priv; - GSettings *settings; - EMailReplyStyle style; CamelMimeMessage *message; + CreateComposerData *ccd; EShellBackend *shell_backend; EShell *shell; @@ -218,17 +282,15 @@ mail_attachment_handler_reply (EAttachmentHandler *handler, message = mail_attachment_handler_get_selected_message (handler); g_return_if_fail (message != NULL); - settings = e_util_ref_settings ("org.gnome.evolution.mail"); - style = g_settings_get_enum (settings, "reply-style-name"); - g_object_unref (settings); - shell_backend = E_SHELL_BACKEND (priv->backend); shell = e_shell_backend_get_shell (shell_backend); - em_utils_reply_to_message ( - shell, message, NULL, NULL, reply_type, style, NULL, NULL); + ccd = g_new0 (CreateComposerData, 1); + ccd->message = message; + ccd->reply_type = reply_type; + ccd->is_reply = TRUE; - g_object_unref (message); + e_msg_composer_new (shell, mail_attachment_handler_composer_created_cb, ccd); } static void @@ -259,6 +321,7 @@ mail_attachment_handler_message_edit (GtkAction *action, EMailAttachmentHandlerPrivate *priv; CamelMimeMessage *message; CamelFolder *folder; + CreateComposerData *ccd; EShell *shell; priv = E_MAIL_ATTACHMENT_HANDLER_GET_PRIVATE (handler); @@ -269,10 +332,11 @@ mail_attachment_handler_message_edit (GtkAction *action, shell = e_shell_backend_get_shell (E_SHELL_BACKEND (priv->backend)); folder = mail_attachment_handler_guess_folder_ref (handler); - em_utils_edit_message (shell, folder, message, NULL, TRUE); + ccd = g_new0 (CreateComposerData, 1); + ccd->message = message; + ccd->folder = folder; - g_clear_object (&folder); - g_object_unref (message); + e_msg_composer_new (shell, mail_attachment_handler_composer_created_cb, ccd); } static void @@ -301,6 +365,7 @@ mail_attachment_handler_redirect (GtkAction *action, { EMailAttachmentHandlerPrivate *priv; CamelMimeMessage *message; + CreateComposerData *ccd; EShell *shell; priv = E_MAIL_ATTACHMENT_HANDLER_GET_PRIVATE (handler); @@ -310,9 +375,12 @@ mail_attachment_handler_redirect (GtkAction *action, shell = e_shell_backend_get_shell (E_SHELL_BACKEND (priv->backend)); - em_utils_redirect_message (shell, message); + ccd = g_new0 (CreateComposerData, 1); + ccd->message = message; + ccd->folder = NULL; + ccd->is_redirect = TRUE; - g_object_unref (message); + e_msg_composer_new (shell, mail_attachment_handler_composer_created_cb, ccd); } static GtkActionEntry standard_entries[] = { diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c index d748f31..506b0d7 100644 --- a/modules/mail/e-mail-shell-backend.c +++ b/modules/mail/e-mail-shell-backend.c @@ -293,6 +293,29 @@ action_mail_account_new_cb (GtkAction *action, } static void +action_mail_message_new_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CamelFolder *folder = user_data; + EMsgComposer *composer; + GError *error = NULL; + + if (folder) + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + em_utils_compose_new_message (composer, folder); + } + + g_clear_object (&folder); +} + +static void action_mail_message_new_cb (GtkAction *action, EShellWindow *shell_window) { @@ -342,8 +365,9 @@ action_mail_message_new_cb (GtkAction *action, g_free (folder_name); } -exit: - em_utils_compose_new_message (shell, folder); + exit: + e_msg_composer_new (shell, action_mail_message_new_composer_created_cb, + folder ? g_object_ref (folder) : NULL); } static GtkActionEntry item_entries[] = { @@ -495,11 +519,11 @@ mail_shell_backend_window_added_cb (GtkApplication *application, /* This applies to both the composer and signature editor. */ if (editor != NULL) { - EHTMLEditorView *view; + EContentEditor *cnt_editor; GSettings *settings; gboolean active = TRUE; - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); settings = e_util_ref_settings ("org.gnome.evolution.mail"); @@ -508,7 +532,7 @@ mail_shell_backend_window_added_cb (GtkApplication *application, g_object_unref (settings); - e_html_editor_view_set_html_mode (view, active); + e_content_editor_set_html_mode (cnt_editor, active); } if (E_IS_MSG_COMPOSER (window)) { diff --git a/modules/mail/e-mail-shell-content.c b/modules/mail/e-mail-shell-content.c index 66a79c1..917c6cf 100644 --- a/modules/mail/e-mail-shell-content.c +++ b/modules/mail/e-mail-shell-content.c @@ -67,6 +67,27 @@ G_DEFINE_DYNAMIC_TYPE_EXTENDED ( E_TYPE_MAIL_READER, e_mail_shell_content_reader_init)) +static gboolean +mail_shell_content_transform_num_attachments_to_visible_boolean_with_settings (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + GSettings *settings; + gboolean res = TRUE; + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + + if (g_settings_get_boolean (settings, "show-attachment-bar")) + res = e_attachment_store_transform_num_attachments_to_visible_boolean (binding, from_value, to_value, user_data); + else + g_value_set_boolean (to_value, FALSE); + + g_clear_object (&settings); + + return res; +} + static void reconnect_changed_event (EMailReader *child, EMailReader *parent) @@ -179,9 +200,11 @@ mail_shell_content_constructed (GObject *object) EMailShellContentPrivate *priv; EShellContent *shell_content; EShellView *shell_view; + EAttachmentStore *attachment_store; + EMailDisplay *display; GtkWindow *window; - GtkWidget *container; GtkWidget *widget; + GtkBox *vbox; priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (object); @@ -193,11 +216,15 @@ mail_shell_content_constructed (GObject *object) /* Build content widgets. */ - container = GTK_WIDGET (object); + widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4); + gtk_container_add (GTK_CONTAINER (shell_content), widget); + gtk_widget_show (widget); + + vbox = GTK_BOX (widget); widget = e_mail_paned_view_new (shell_view); + gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0); - gtk_container_add (GTK_CONTAINER (container), widget); priv->mail_view = g_object_ref (widget); gtk_widget_show (widget); @@ -208,6 +235,17 @@ mail_shell_content_constructed (GObject *object) widget, "folder-loaded", G_CALLBACK (reconnect_folder_loaded_event), object); + display = e_mail_reader_get_mail_display (E_MAIL_READER (object)); + attachment_store = e_mail_display_get_attachment_store (display); + widget = GTK_WIDGET (e_mail_display_get_attachment_view (display)); + + e_binding_bind_property_full ( + attachment_store, "num-attachments", + widget, "visible", + G_BINDING_SYNC_CREATE, + mail_shell_content_transform_num_attachments_to_visible_boolean_with_settings, + NULL, NULL, NULL); + window = e_mail_reader_get_window (E_MAIL_READER (object)); widget = e_mail_reader_get_message_list (E_MAIL_READER (object)); diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c index fc6cebf..d843671 100644 --- a/modules/mail/e-mail-shell-view-actions.c +++ b/modules/mail/e-mail-shell-view-actions.c @@ -256,6 +256,29 @@ action_mail_create_search_folder_cb (GtkAction *action, } static void +action_mail_attachment_bar_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailDisplay *mail_display; + EAttachmentView *attachment_view; + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + mail_display = e_mail_reader_get_mail_display (E_MAIL_READER (mail_shell_view->priv->mail_shell_content)); + attachment_view = e_mail_display_get_attachment_view (mail_display); + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) { + EAttachmentStore *store; + guint num_attachments; + + store = e_attachment_bar_get_store (E_ATTACHMENT_BAR (attachment_view)); + num_attachments = e_attachment_store_get_num_attachments (store); + gtk_widget_set_visible (GTK_WIDGET (attachment_view), num_attachments > 0); + } else { + gtk_widget_hide (GTK_WIDGET (attachment_view)); + } +} + +static void action_mail_download_finished_cb (CamelStore *store, GAsyncResult *result, EActivity *activity) @@ -2025,6 +2048,14 @@ static GtkToggleActionEntry mail_toggle_entries[] = { NULL, /* Handled by property bindings */ TRUE }, + { "mail-attachment-bar", + NULL, + N_("Show _Attachment Bar"), + NULL, + N_("Show Attachment Bar below the message preview pane when the message has attachments"), + G_CALLBACK (action_mail_attachment_bar_cb), + TRUE }, + { "mail-show-deleted", NULL, N_("Show _Deleted Messages"), @@ -2334,6 +2365,11 @@ e_mail_shell_view_actions_init (EMailShellView *mail_shell_view) ACTION (MAIL_VFOLDER_UNMATCHED_ENABLE), "active", G_SETTINGS_BIND_DEFAULT); + g_settings_bind ( + settings, "show-attachment-bar", + ACTION (MAIL_ATTACHMENT_BAR), "active", + G_SETTINGS_BIND_DEFAULT); + g_object_unref (settings); /* Fine tuning. */ diff --git a/modules/mail/e-mail-shell-view-actions.h b/modules/mail/e-mail-shell-view-actions.h index 866cc98..5459f73 100644 --- a/modules/mail/e-mail-shell-view-actions.h +++ b/modules/mail/e-mail-shell-view-actions.h @@ -34,6 +34,8 @@ E_SHELL_WINDOW_ACTION ((window), "mail-account-refresh") #define E_SHELL_WINDOW_ACTION_MAIL_ADD_SENDER(window) \ E_SHELL_WINDOW_ACTION ((window), "mail-add-sender") +#define E_SHELL_WINDOW_ACTION_MAIL_ATTACHMENT_BAR(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-attachment-bar") #define E_SHELL_WINDOW_ACTION_MAIL_CARET_MODE(window) \ E_SHELL_WINDOW_ACTION ((window), "mail-caret-mode") #define E_SHELL_WINDOW_ACTION_MAIL_CHECK_FOR_JUNK(window) \ diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c index caf2392..e3c5ec1 100644 --- a/modules/mail/e-mail-shell-view-private.c +++ b/modules/mail/e-mail-shell-view-private.c @@ -250,6 +250,33 @@ mail_shell_view_folder_tree_popup_event_cb (EShellView *shell_view, } static gboolean +mail_shell_view_mail_display_needs_key (EMailShellView *mail_shell_view, + EMailDisplay *mail_display) +{ + if (gtk_widget_has_focus (GTK_WIDGET (mail_display))) { + GDBusProxy *web_extension; + + /* Intentionally use Evolution Web Extension */ + web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (mail_display)); + if (web_extension) { + GVariant *result; + + result = g_dbus_proxy_get_cached_property (web_extension, "NeedInput"); + if (result) { + gboolean need_input; + + need_input = g_variant_get_boolean (result); + g_variant_unref (result); + + return need_input; + } + } + } + + return FALSE; +} + +static gboolean mail_shell_view_key_press_event_cb (EMailShellView *mail_shell_view, GdkEventKey *event) { @@ -281,42 +308,11 @@ mail_shell_view_key_press_event_cb (EMailShellView *mail_shell_view, action = ACTION (MAIL_SMART_BACKWARD); break; - case GDK_KEY_Home: - case GDK_KEY_Left: - case GDK_KEY_Up: - case GDK_KEY_Right: - case GDK_KEY_Down: - case GDK_KEY_Next: - case GDK_KEY_End: - case GDK_KEY_Begin: - /* If Caret mode is enabled don't try to process these keys */ - if (e_web_view_get_caret_mode (E_WEB_VIEW (mail_display))) - return FALSE; - case GDK_KEY_Prior: - if (!e_mail_display_needs_key (mail_display, FALSE) && - webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (mail_display)) != - webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (mail_display))) { - WebKitDOMDocument *document; - WebKitDOMDOMWindow *window; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (mail_display)); - window = webkit_dom_document_get_default_view (document); - - /* Workaround WebKit bug for key navigation, when inner IFRAME is focused. - * EMailView's inner IFRAMEs have disabled scrolling, but WebKit doesn't post - * key navigation events to parent's frame, thus the view doesn't scroll. - * This is a poor workaround for this issue, the main frame is focused, - * which has scrolling enabled. - */ - webkit_dom_dom_window_focus (window); - } - - return FALSE; default: return FALSE; } - if (e_mail_display_needs_key (mail_display, TRUE)) + if (mail_shell_view_mail_display_needs_key (mail_shell_view, mail_display)) return FALSE; gtk_action_activate (action); diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h index 4300742..c2eb113 100644 --- a/modules/mail/e-mail-shell-view-private.h +++ b/modules/mail/e-mail-shell-view-private.h @@ -167,6 +167,8 @@ void e_mail_shell_view_update_sidebar (EMailShellView *mail_shell_view); void e_mail_shell_view_update_send_receive_menus (EMailShellView *mail_shell_view); +GDBusProxy * e_mail_shell_view_get_web_extension_proxy + (EMailShellView *mail_shell_view); G_END_DECLS diff --git a/modules/mail/em-composer-prefs.c b/modules/mail/em-composer-prefs.c index 11d9172..ecdcf40 100644 --- a/modules/mail/em-composer-prefs.c +++ b/modules/mail/em-composer-prefs.c @@ -141,11 +141,10 @@ spell_language_save (EMComposerPrefs *prefs) static void spell_setup (EMComposerPrefs *prefs) { - GList *list, *link; + GList *list = NULL, *link; GtkListStore *store; store = GTK_LIST_STORE (prefs->language_model); - list = e_spell_checker_list_available_dicts (prefs->spell_checker); /* Populate the GtkListStore. */ diff --git a/modules/prefer-plain/e-mail-display-popup-prefer-plain.c b/modules/prefer-plain/e-mail-display-popup-prefer-plain.c index c23e5d2..21fc1ce 100644 --- a/modules/prefer-plain/e-mail-display-popup-prefer-plain.c +++ b/modules/prefer-plain/e-mail-display-popup-prefer-plain.c @@ -35,12 +35,11 @@ typedef struct _EMailDisplayPopupPreferPlainClass EMailDisplayPopupPreferPlainCl struct _EMailDisplayPopupPreferPlain { EExtension parent; - WebKitDOMDocument *document; gchar *text_plain_id; gchar *text_html_id; + gchar *document_uri; GtkActionGroup *action_group; - }; struct _EMailDisplayPopupPreferPlainClass { @@ -94,16 +93,20 @@ toggle_part (GtkAction *action, EMailDisplayPopupExtension *extension) { EMailDisplayPopupPreferPlain *pp_extension = (EMailDisplayPopupPreferPlain *) extension; - WebKitDOMDocument *doc = pp_extension->document; - WebKitDOMDOMWindow *window; - WebKitDOMElement *frame_element; SoupURI *soup_uri; GHashTable *query; gchar *uri; - uri = webkit_dom_document_get_document_uri (doc); - soup_uri = soup_uri_new (uri); - g_free (uri); + if (!pp_extension->document_uri) + return; + + soup_uri = soup_uri_new (pp_extension->document_uri); + + if (!soup_uri || !soup_uri->query) { + if (soup_uri) + soup_uri_free (soup_uri); + return; + } query = soup_form_decode (soup_uri->query); g_hash_table_replace ( @@ -123,11 +126,8 @@ toggle_part (GtkAction *action, uri = soup_uri_to_string (soup_uri, FALSE); soup_uri_free (soup_uri); - /* Get frame's window and from the window the actual <iframe> element */ - window = webkit_dom_document_get_default_view (doc); - frame_element = webkit_dom_dom_window_get_frame_element (window); - webkit_dom_html_iframe_element_set_src ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame_element), uri); + e_web_view_set_document_iframe_src (E_WEB_VIEW (e_extension_get_extensible (E_EXTENSION (extension))), + pp_extension->document_uri, uri); g_free (uri); } @@ -169,6 +169,17 @@ set_text_html_id (EMailDisplayPopupPreferPlain *extension, extension->text_html_id = g_strdup (id); } +static void +set_document_uri (EMailDisplayPopupPreferPlain *extension, + const gchar *document_uri) +{ + if (extension->document_uri == document_uri) + return; + + g_free (extension->document_uri); + extension->document_uri = g_strdup (document_uri); +} + static GtkActionGroup * create_group (EMailDisplayPopupExtension *extension) { @@ -218,18 +229,17 @@ create_group (EMailDisplayPopupExtension *extension) static void mail_display_popup_prefer_plain_update_actions (EMailDisplayPopupExtension *extension, - WebKitHitTestResult *context) + const gchar *popup_document_uri) { EMailDisplay *display; + EMailDisplayPopupPreferPlain *pp_extension; GtkAction *action; - WebKitDOMNode *node; - gchar *uri, *part_id, *pos, *prefix; + gchar *part_id, *pos, *prefix; SoupURI *soup_uri; GHashTable *query; EMailPartList *part_list; gboolean is_text_plain; const gchar *action_name; - EMailDisplayPopupPreferPlain *pp_extension; GQueue queue = G_QUEUE_INIT; GList *head, *link; @@ -241,22 +251,17 @@ mail_display_popup_prefer_plain_update_actions (EMailDisplayPopupExtension *exte if (!pp_extension->action_group) pp_extension->action_group = create_group (extension); - g_object_get (context, "inner-node", &node, NULL); + set_document_uri (pp_extension, popup_document_uri); - if (!node) { - gtk_action_group_set_visible (pp_extension->action_group, FALSE); - return; - } + if (pp_extension->document_uri) + soup_uri = soup_uri_new (pp_extension->document_uri); + else + soup_uri = NULL; - pp_extension->document = webkit_dom_node_get_owner_document (node); - uri = webkit_dom_document_get_document_uri (pp_extension->document); - - soup_uri = soup_uri_new (uri); if (!soup_uri || !soup_uri->query) { gtk_action_group_set_visible (pp_extension->action_group, FALSE); if (soup_uri) soup_uri_free (soup_uri); - g_free (uri); return; } @@ -264,28 +269,19 @@ mail_display_popup_prefer_plain_update_actions (EMailDisplayPopupExtension *exte part_id = g_hash_table_lookup (query, "part_id"); if (part_id == NULL) { gtk_action_group_set_visible (pp_extension->action_group, FALSE); - g_hash_table_destroy (query); - soup_uri_free (soup_uri); - g_free (uri); - return; + goto out; } pos = strstr (part_id, ".alternative-prefer-plain."); if (!pos) { gtk_action_group_set_visible (pp_extension->action_group, FALSE); - g_hash_table_destroy (query); - soup_uri_free (soup_uri); - g_free (uri); - return; + goto out; } /* Don't display the actions on any other than text/plain or text/html parts */ if (!strstr (pos, "plain_text") && !strstr (pos, "text_html")) { gtk_action_group_set_visible (pp_extension->action_group, FALSE); - g_hash_table_destroy (query); - soup_uri_free (soup_uri); - g_free (uri); - return; + goto out; } /* Check whether the displayed part is text_plain */ @@ -353,9 +349,9 @@ mail_display_popup_prefer_plain_update_actions (EMailDisplayPopupExtension *exte } g_free (prefix); + out: g_hash_table_destroy (query); soup_uri_free (soup_uri); - g_free (uri); } void @@ -377,8 +373,7 @@ e_mail_display_popup_prefer_plain_dispose (GObject *object) } /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_mail_display_popup_prefer_plain_parent_class)-> - dispose (object); + G_OBJECT_CLASS (e_mail_display_popup_prefer_plain_parent_class)->dispose (object); } static void @@ -390,10 +385,10 @@ e_mail_display_popup_prefer_plain_finalize (GObject *object) g_free (extension->text_html_id); g_free (extension->text_plain_id); + g_free (extension->document_uri); /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_mail_display_popup_prefer_plain_parent_class)-> - finalize (object); + G_OBJECT_CLASS (e_mail_display_popup_prefer_plain_parent_class)->finalize (object); } static void @@ -419,7 +414,6 @@ e_mail_display_popup_extension_interface_init (EMailDisplayPopupExtensionInterfa void e_mail_display_popup_prefer_plain_class_finalize (EMailDisplayPopupPreferPlainClass *class) { - } static void @@ -428,5 +422,5 @@ e_mail_display_popup_prefer_plain_init (EMailDisplayPopupPreferPlain *extension) extension->action_group = NULL; extension->text_html_id = NULL; extension->text_plain_id = NULL; - extension->document = NULL; + extension->document_uri = NULL; } diff --git a/modules/settings/Makefile.am b/modules/settings/Makefile.am index e137bea..45db4e8 100644 --- a/modules/settings/Makefile.am +++ b/modules/settings/Makefile.am @@ -25,8 +25,8 @@ module_settings_la_SOURCES = \ e-settings-date-edit.h \ e-settings-deprecated.c \ e-settings-deprecated.h \ - e-settings-html-editor-view.c \ - e-settings-html-editor-view.h \ + e-settings-content-editor.c \ + e-settings-content-editor.h \ e-settings-mail-browser.c \ e-settings-mail-browser.h \ e-settings-mail-formatter.c \ diff --git a/modules/settings/e-settings-content-editor.c b/modules/settings/e-settings-content-editor.c new file mode 100644 index 0000000..3bc438c --- /dev/null +++ b/modules/settings/e-settings-content-editor.c @@ -0,0 +1,224 @@ +/* + * e-settings-content-editor.c + * + * 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. + * + * 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 General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include "e-settings-content-editor.h" + +#include <e-util/e-util.h> + +#define E_SETTINGS_CONTENT_EDITOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SETTINGS_CONTENT_EDITOR, ESettingsContentEditorPrivate)) + +struct _ESettingsContentEditorPrivate { + GSettings *settings; + + GHashTable *old_settings; +}; + +G_DEFINE_DYNAMIC_TYPE ( + ESettingsContentEditor, + e_settings_content_editor, + E_TYPE_EXTENSION) + +static void +settings_content_editor_inline_spelling_changed (ESettingsContentEditor *extension, + gboolean spell_check_enabled) +{ + EExtensible *extensible; + EContentEditor *cnt_editor; + + extensible = e_extension_get_extensible (E_EXTENSION (extension)); + cnt_editor = e_html_editor_get_content_editor (E_HTML_EDITOR (extensible)); + + e_content_editor_set_spell_check_enabled (cnt_editor, spell_check_enabled); +} + +static void +settings_content_editor_load_style (ESettingsContentEditor *extension) +{ + EExtensible *extensible; + EContentEditor *cnt_editor; + + extensible = e_extension_get_extensible (E_EXTENSION (extension)); + cnt_editor = e_html_editor_get_content_editor (E_HTML_EDITOR (extensible)); + e_content_editor_update_styles (cnt_editor); +} + +static void +settings_content_editor_changed_cb (GSettings *settings, + const gchar *key, + ESettingsContentEditor *extension) +{ + GVariant *new_value, *old_value; + + new_value = g_settings_get_value (settings, key); + old_value = g_hash_table_lookup (extension->priv->old_settings, key); + + if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) { + if (new_value) + g_hash_table_insert (extension->priv->old_settings, g_strdup (key), new_value); + else + g_hash_table_remove (extension->priv->old_settings, key); + + if (g_strcmp0 (key, "composer-inline-spelling") == 0) + settings_content_editor_inline_spelling_changed (extension, g_settings_get_boolean (settings, key)); + else + settings_content_editor_load_style (extension); + } else if (new_value) { + g_variant_unref (new_value); + } +} + +static void +settings_content_editor_html_editor_realize_cb (GtkWidget *html_editor, + ESettingsContentEditor *extension) +{ + GSettings *settings; + + settings = extension->priv->settings; + + settings_content_editor_inline_spelling_changed (extension, g_settings_get_boolean (settings, "composer-inline-spelling")); + settings_content_editor_load_style (extension); + + /* Reload the web view when certain settings change. */ + + g_signal_connect ( + settings, "changed::use-custom-font", + G_CALLBACK (settings_content_editor_changed_cb), extension); + + g_signal_connect ( + settings, "changed::monospace-font", + G_CALLBACK (settings_content_editor_changed_cb), extension); + + g_signal_connect ( + settings, "changed::variable-width-font", + G_CALLBACK (settings_content_editor_changed_cb), extension); + + g_signal_connect ( + settings, "changed::mark-citations", + G_CALLBACK (settings_content_editor_changed_cb), extension); + + g_signal_connect ( + settings, "changed::citation-color", + G_CALLBACK (settings_content_editor_changed_cb), extension); + + g_signal_connect ( + settings, "changed::composer-inline-spelling", + G_CALLBACK (settings_content_editor_changed_cb), extension); +} + +static void +settings_content_editor_dispose (GObject *object) +{ + ESettingsContentEditorPrivate *priv; + + priv = E_SETTINGS_CONTENT_EDITOR_GET_PRIVATE (object); + + if (priv->settings != NULL) { + g_signal_handlers_disconnect_by_func ( + priv->settings, + settings_content_editor_changed_cb, object); + } + + g_clear_object (&priv->settings); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_settings_content_editor_parent_class)->dispose (object); +} + +static void +settings_content_editor_finalize (GObject *object) +{ + ESettingsContentEditorPrivate *priv; + + priv = E_SETTINGS_CONTENT_EDITOR_GET_PRIVATE (object); + + if (priv->old_settings) { + g_hash_table_destroy (priv->old_settings); + priv->old_settings = NULL; + } + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_settings_content_editor_parent_class)->finalize (object); +} + +static void +settings_content_editor_constructed (GObject *object) +{ + EExtensible *extensible; + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_settings_content_editor_parent_class)->constructed (object); + + extensible = e_extension_get_extensible (E_EXTENSION (object)); + + g_signal_connect ( + extensible, "realize", + G_CALLBACK (settings_content_editor_html_editor_realize_cb), object); +} + +static void +e_settings_content_editor_class_init (ESettingsContentEditorClass *class) +{ + GObjectClass *object_class; + EExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESettingsContentEditorPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = settings_content_editor_dispose; + object_class->finalize = settings_content_editor_finalize; + object_class->constructed = settings_content_editor_constructed; + + extension_class = E_EXTENSION_CLASS (class); + extension_class->extensible_type = E_TYPE_HTML_EDITOR; +} + +static void +e_settings_content_editor_class_finalize (ESettingsContentEditorClass *class) +{ +} + +static void +e_settings_content_editor_init (ESettingsContentEditor *extension) +{ + GSettings *settings; + + extension->priv = E_SETTINGS_CONTENT_EDITOR_GET_PRIVATE (extension); + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + extension->priv->settings = settings; + + extension->priv->old_settings = g_hash_table_new_full ( + g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); +} + +void +e_settings_content_editor_type_register (GTypeModule *type_module) +{ + /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration + * function, so we have to wrap it with a public function in + * order to register types from a separate compilation unit. */ + e_settings_content_editor_register_type (type_module); +} diff --git a/modules/settings/e-settings-content-editor.h b/modules/settings/e-settings-content-editor.h new file mode 100644 index 0000000..2b5dc90 --- /dev/null +++ b/modules/settings/e-settings-content-editor.h @@ -0,0 +1,64 @@ +/* + * e-settings-content-editor.h + * + * 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. + * + * 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 General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef E_SETTINGS_CONTENT_EDITOR_H +#define E_SETTINGS_CONTENT_EDITOR_H + +#include <libebackend/libebackend.h> + +/* Standard GObject macros */ +#define E_TYPE_SETTINGS_CONTENT_EDITOR \ + (e_settings_content_editor_get_type ()) +#define E_SETTINGS_CONTENT_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SETTINGS_CONTENT_EDITOR, ESettingsContentEditor)) +#define E_SETTINGS_CONTENT_EDITOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SETTINGS_CONTENT_EDITOR, ESettingsContentEditorClass)) +#define E_IS_SETTINGS_CONTENT_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SETTINGS_CONTENT_EDITOR)) +#define E_IS_SETTINGS_CONTENT_EDITOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SETTINGS_CONTENT_EDITOR)) +#define E_SETTINGS_CONTENT_EDITOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SETTINGS_CONTENT_EDITOR, ESettingsContentEditorClass)) + +G_BEGIN_DECLS + +typedef struct _ESettingsContentEditor ESettingsContentEditor; +typedef struct _ESettingsContentEditorClass ESettingsContentEditorClass; +typedef struct _ESettingsContentEditorPrivate ESettingsContentEditorPrivate; + +struct _ESettingsContentEditor { + EExtension parent; + ESettingsContentEditorPrivate *priv; +}; + +struct _ESettingsContentEditorClass { + EExtensionClass parent_class; +}; + +GType e_settings_content_editor_get_type + (void) G_GNUC_CONST; +void e_settings_content_editor_type_register + (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_SETTINGS_CONTENT_EDITOR_H */ diff --git a/modules/settings/e-settings-html-editor-view.c b/modules/settings/e-settings-html-editor-view.c deleted file mode 100644 index 6367767..0000000 --- a/modules/settings/e-settings-html-editor-view.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * e-settings-html-editor-web-view.c - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <string.h> - -#include "e-settings-html-editor-view.h" - -#include <e-util/e-util.h> - -#define E_SETTINGS_HTML_EDITOR_VIEW_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_SETTINGS_HTML_EDITOR_VIEW, ESettingsHTMLEditorViewPrivate)) - -struct _ESettingsHTMLEditorViewPrivate { - GSettings *settings; - - GHashTable *old_settings; -}; - -G_DEFINE_DYNAMIC_TYPE ( - ESettingsHTMLEditorView, - e_settings_html_editor_view, - E_TYPE_EXTENSION) - -static void -settings_html_editor_view_load_style (ESettingsHTMLEditorView *extension) -{ - EExtensible *extensible; - - extensible = e_extension_get_extensible (E_EXTENSION (extension)); - - e_html_editor_view_update_fonts (E_HTML_EDITOR_VIEW (extensible)); -} - -static void -settings_html_editor_view_changed_cb (GSettings *settings, - const gchar *key, - ESettingsHTMLEditorView *extension) -{ - GVariant *new_value, *old_value; - - new_value = g_settings_get_value (settings, key); - old_value = g_hash_table_lookup (extension->priv->old_settings, key); - - if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) { - if (new_value) - g_hash_table_insert (extension->priv->old_settings, g_strdup (key), new_value); - else - g_hash_table_remove (extension->priv->old_settings, key); - - settings_html_editor_view_load_style (extension); - } else if (new_value) { - g_variant_unref (new_value); - } -} - -static void -settings_html_editor_view_realize (GtkWidget *widget, - ESettingsHTMLEditorView *extension) -{ - GSettings *settings; - - settings = extension->priv->settings; - - g_settings_bind ( - settings, "composer-inline-spelling", - widget, "inline-spelling", - G_SETTINGS_BIND_DEFAULT); - - g_settings_bind ( - settings, "composer-magic-links", - widget, "magic-links", - G_SETTINGS_BIND_DEFAULT); - - g_settings_bind ( - settings, "composer-magic-smileys", - widget, "magic-smileys", - G_SETTINGS_BIND_DEFAULT); - - g_settings_bind ( - settings, "composer-unicode-smileys", - widget, "unicode-smileys", - G_SETTINGS_BIND_DEFAULT); - - settings_html_editor_view_load_style (extension); - - /* Reload the web view when certain settings change. */ - - g_signal_connect ( - settings, "changed::use-custom-font", - G_CALLBACK (settings_html_editor_view_changed_cb), extension); - - g_signal_connect ( - settings, "changed::monospace-font", - G_CALLBACK (settings_html_editor_view_changed_cb), extension); - - g_signal_connect ( - settings, "changed::variable-width-font", - G_CALLBACK (settings_html_editor_view_changed_cb), extension); - - g_signal_connect ( - settings, "changed::mark-citations", - G_CALLBACK (settings_html_editor_view_changed_cb), extension); - - g_signal_connect ( - settings, "changed::citation-color", - G_CALLBACK (settings_html_editor_view_changed_cb), extension); -} - -static void -settings_html_editor_view_dispose (GObject *object) -{ - ESettingsHTMLEditorViewPrivate *priv; - - priv = E_SETTINGS_HTML_EDITOR_VIEW_GET_PRIVATE (object); - - if (priv->settings != NULL) { - g_signal_handlers_disconnect_by_func ( - priv->settings, - settings_html_editor_view_changed_cb, object); - } - - g_clear_object (&priv->settings); - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_settings_html_editor_view_parent_class)->dispose (object); -} - -static void -settings_html_editor_view_finalize (GObject *object) -{ - ESettingsHTMLEditorViewPrivate *priv; - - priv = E_SETTINGS_HTML_EDITOR_VIEW_GET_PRIVATE (object); - - if (priv->old_settings) { - g_hash_table_destroy (priv->old_settings); - priv->old_settings = NULL; - } - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_settings_html_editor_view_parent_class)->finalize (object); -} - -static void -settings_html_editor_view_constructed (GObject *object) -{ - EExtensible *extensible; - - extensible = e_extension_get_extensible (E_EXTENSION (object)); - - g_signal_connect ( - extensible, "realize", - G_CALLBACK (settings_html_editor_view_realize), object); - - /* Chain up to parent's constructed() method. */ - G_OBJECT_CLASS (e_settings_html_editor_view_parent_class)->constructed (object); -} - -static void -e_settings_html_editor_view_class_init (ESettingsHTMLEditorViewClass *class) -{ - GObjectClass *object_class; - EExtensionClass *extension_class; - - g_type_class_add_private (class, sizeof (ESettingsHTMLEditorViewPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->dispose = settings_html_editor_view_dispose; - object_class->finalize = settings_html_editor_view_finalize; - object_class->constructed = settings_html_editor_view_constructed; - - extension_class = E_EXTENSION_CLASS (class); - extension_class->extensible_type = E_TYPE_HTML_EDITOR_VIEW; -} - -static void -e_settings_html_editor_view_class_finalize (ESettingsHTMLEditorViewClass *class) -{ -} - -static void -e_settings_html_editor_view_init (ESettingsHTMLEditorView *extension) -{ - GSettings *settings; - - extension->priv = E_SETTINGS_HTML_EDITOR_VIEW_GET_PRIVATE (extension); - - settings = e_util_ref_settings ("org.gnome.evolution.mail"); - extension->priv->settings = settings; - - extension->priv->old_settings = g_hash_table_new_full ( - g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); -} - -void -e_settings_html_editor_view_type_register (GTypeModule *type_module) -{ - /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration - * function, so we have to wrap it with a public function in - * order to register types from a separate compilation unit. */ - e_settings_html_editor_view_register_type (type_module); -} - diff --git a/modules/settings/e-settings-html-editor-view.h b/modules/settings/e-settings-html-editor-view.h deleted file mode 100644 index 1848794..0000000 --- a/modules/settings/e-settings-html-editor-view.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * e-settings-html-editor-view.h - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifndef E_SETTINGS_HTML_EDITOR_VIEW_H -#define E_SETTINGS_HTML_EDITOR_VIEW_H - -#include <libebackend/libebackend.h> - -/* Standard GObject macros */ -#define E_TYPE_SETTINGS_HTML_EDITOR_VIEW \ - (e_settings_html_editor_view_get_type ()) -#define E_SETTINGS_HTML_EDITOR_VIEW(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_SETTINGS_HTML_EDITOR_VIEW, ESettingsHTMLEditorView)) -#define E_SETTINGS_HTML_EDITOR_VIEW_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_SETTINGS_HTML_EDITOR_VIEW, ESettingsHTMLEditorViewClass)) -#define E_IS_SETTINGS_HTML_EDITOR_VIEW(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_SETTINGS_HTML_EDITOR_VIEW)) -#define E_IS_SETTINGS_HTML_EDITOR_VIEW_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_SETTINGS_HTML_EDITOR_VIEW)) -#define E_SETTINGS_HTML_EDITOR_VIEW_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_SETTINGS_HTML_EDITOR_VIEW, ESettingsHTMLEditorViewClass)) - -G_BEGIN_DECLS - -typedef struct _ESettingsHTMLEditorView ESettingsHTMLEditorView; -typedef struct _ESettingsHTMLEditorViewClass ESettingsHTMLEditorViewClass; -typedef struct _ESettingsHTMLEditorViewPrivate ESettingsHTMLEditorViewPrivate; - -struct _ESettingsHTMLEditorView { - EExtension parent; - ESettingsHTMLEditorViewPrivate *priv; -}; - -struct _ESettingsHTMLEditorViewClass { - EExtensionClass parent_class; -}; - -GType e_settings_html_editor_view_get_type - (void) G_GNUC_CONST; -void e_settings_html_editor_view_type_register - (GTypeModule *type_module); - -G_END_DECLS - -#endif /* E_SETTINGS_HTML_EDITOR_VIEW_H */ diff --git a/modules/settings/e-settings-spell-checker.c b/modules/settings/e-settings-spell-checker.c index 8192766..812e3b0 100644 --- a/modules/settings/e-settings-spell-checker.c +++ b/modules/settings/e-settings-spell-checker.c @@ -62,8 +62,7 @@ settings_spell_checker_constructed (GObject *object) E_SETTINGS_SPELL_CHECKER (object)); /* Make sure there are no active languages at this point. */ - g_warn_if_fail ( - e_spell_checker_count_active_languages (spell_checker) == 0); + g_warn_if_fail (e_spell_checker_count_active_languages (spell_checker) == 0); settings = e_util_ref_settings ("org.gnome.evolution.mail"); strv = g_settings_get_strv (settings, "composer-spell-languages"); diff --git a/modules/settings/evolution-module-settings.c b/modules/settings/evolution-module-settings.c index 5b81a94..8ade039 100644 --- a/modules/settings/evolution-module-settings.c +++ b/modules/settings/evolution-module-settings.c @@ -19,9 +19,10 @@ #include "e-settings-calendar-item.h" #include "e-settings-calendar-view.h" #include "e-settings-client-cache.h" +#include "e-settings-content-editor.h" #include "e-settings-date-edit.h" #include "e-settings-deprecated.h" -#include "e-settings-html-editor-view.h" +#include "e-settings-content-editor.h" #include "e-settings-mail-browser.h" #include "e-settings-mail-formatter.h" #include "e-settings-mail-part-headers.h" @@ -46,9 +47,10 @@ e_module_load (GTypeModule *type_module) e_settings_calendar_item_type_register (type_module); e_settings_calendar_view_type_register (type_module); e_settings_client_cache_type_register (type_module); + e_settings_content_editor_type_register (type_module); e_settings_date_edit_type_register (type_module); e_settings_deprecated_type_register (type_module); - e_settings_html_editor_view_type_register (type_module); + e_settings_content_editor_type_register (type_module); e_settings_mail_browser_type_register (type_module); e_settings_mail_formatter_type_register (type_module); e_settings_mail_part_headers_type_register (type_module); diff --git a/modules/text-highlight/e-mail-display-popup-text-highlight.c b/modules/text-highlight/e-mail-display-popup-text-highlight.c index eb072a0..a28932e 100644 --- a/modules/text-highlight/e-mail-display-popup-text-highlight.c +++ b/modules/text-highlight/e-mail-display-popup-text-highlight.c @@ -36,7 +36,8 @@ typedef struct _EMailDisplayPopupTextHighlight { GtkActionGroup *action_group; - WebKitDOMDocument *document; + volatile gint updating; + gchar *document_uri; } EMailDisplayPopupTextHighlight; typedef struct _EMailDisplayPopupTextHighlightClass { @@ -107,33 +108,42 @@ static GtkActionEntry entries[] = { }; static void +set_document_uri (EMailDisplayPopupTextHighlight *extension, + const gchar *document_uri) +{ + if (extension->document_uri == document_uri) + return; + + g_free (extension->document_uri); + extension->document_uri = g_strdup (document_uri); +} + +static void reformat (GtkAction *old, GtkAction *action, gpointer user_data) { EMailDisplayPopupTextHighlight *th_extension; - WebKitDOMDocument *doc; - WebKitDOMDOMWindow *window; - WebKitDOMElement *frame_element; SoupURI *soup_uri; GHashTable *query; gchar *uri; th_extension = E_MAIL_DISPLAY_POPUP_TEXT_HIGHLIGHT (user_data); - doc = th_extension->document; - if (!doc) + + if (g_atomic_int_get (&th_extension->updating)) return; - uri = webkit_dom_document_get_document_uri (doc); - soup_uri = soup_uri_new (uri); - g_free (uri); + if (th_extension->document_uri) + soup_uri = soup_uri_new (th_extension->document_uri); + else + soup_uri = NULL; if (!soup_uri) - goto exit; + return; if (!soup_uri->query) { soup_uri_free (soup_uri); - goto exit; + return; } query = soup_form_decode (soup_uri->query); @@ -148,17 +158,10 @@ reformat (GtkAction *old, uri = soup_uri_to_string (soup_uri, FALSE); soup_uri_free (soup_uri); - /* Get frame's window and from the window the actual <iframe> element */ - window = webkit_dom_document_get_default_view (doc); - frame_element = webkit_dom_dom_window_get_frame_element (window); - webkit_dom_html_iframe_element_set_src ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame_element), uri); + e_web_view_set_document_iframe_src (E_WEB_VIEW (e_extension_get_extensible (E_EXTENSION (th_extension))), + th_extension->document_uri, uri); g_free (uri); - - /* The frame has been reloaded, the document pointer is invalid now */ -exit: - th_extension->document = NULL; } static GtkActionGroup * @@ -215,10 +218,12 @@ create_group (EMailDisplayPopupExtension *extension) NULL, NULL, action_index); action_index++; gtk_action_group_add_action (group, GTK_ACTION (action)); - g_signal_connect ( - action, "changed", - G_CALLBACK (reformat), extension); - gtk_radio_action_set_group (action, radio_group); + if (radio_group) + gtk_radio_action_set_group (action, radio_group); + else + g_signal_connect ( + action, "changed", + G_CALLBACK (reformat), extension); radio_group = gtk_radio_action_get_group (action); g_object_unref (action); @@ -246,11 +251,13 @@ create_group (EMailDisplayPopupExtension *extension) NULL, NULL, action_index); action_index++; gtk_action_group_add_action (group, GTK_ACTION (action)); - g_signal_connect ( - action, "changed", - G_CALLBACK (reformat), extension); - gtk_radio_action_set_group (action, radio_group); + if (radio_group) + gtk_radio_action_set_group (action, radio_group); + else + g_signal_connect ( + action, "changed", + G_CALLBACK (reformat), extension); radio_group = gtk_radio_action_get_group (action); g_object_unref (action); @@ -273,33 +280,26 @@ create_group (EMailDisplayPopupExtension *extension) static void update_actions (EMailDisplayPopupExtension *extension, - WebKitHitTestResult *context) + const gchar *popup_document_uri) { EMailDisplayPopupTextHighlight *th_extension; - WebKitDOMNode *node; - WebKitDOMDocument *document; - gchar *uri; th_extension = E_MAIL_DISPLAY_POPUP_TEXT_HIGHLIGHT (extension); - if (th_extension->action_group == NULL) { + if (!th_extension->action_group) th_extension->action_group = create_group (extension); - } - th_extension->document = NULL; - g_object_get (G_OBJECT (context), "inner-node", &node, NULL); - document = webkit_dom_node_get_owner_document (node); - uri = webkit_dom_document_get_document_uri (document); + set_document_uri (th_extension, popup_document_uri); /* If the part below context menu was made by text-highlight formatter, * then try to check what formatter it's using at the moment and set * it as active in the popup menu */ - if (uri && strstr (uri, ".text-highlight") != NULL) { + if (th_extension->document_uri && strstr (th_extension->document_uri, ".text-highlight") != NULL) { SoupURI *soup_uri; gtk_action_group_set_visible ( th_extension->action_group, TRUE); - soup_uri = soup_uri_new (uri); + soup_uri = soup_uri_new (th_extension->document_uri); if (soup_uri && soup_uri->query) { GHashTable *query = soup_form_decode (soup_uri->query); gchar *highlighter; @@ -310,31 +310,23 @@ update_actions (EMailDisplayPopupExtension *extension, th_extension->action_group, highlighter); if (action) { gint value; + g_atomic_int_add (&th_extension->updating, 1); g_object_get ( G_OBJECT (action), "value", &value, NULL); gtk_radio_action_set_current_value ( GTK_RADIO_ACTION (action), value); + g_atomic_int_add (&th_extension->updating, -1); } } g_hash_table_destroy (query); } - if (soup_uri) { + if (soup_uri) soup_uri_free (soup_uri); - } - } else { - gtk_action_group_set_visible ( - th_extension->action_group, FALSE); + gtk_action_group_set_visible (th_extension->action_group, FALSE); } - - /* Set the th_extension->document AFTER changing the active action to - * prevent the reformat() from doing some crazy reformatting - * (reformat() returns immediatelly when th_extension->document is NULL) */ - th_extension->document = document; - - g_free (uri); } void @@ -363,11 +355,11 @@ e_mail_display_popup_extension_interface_init (EMailDisplayPopupExtensionInterfa void e_mail_display_popup_text_highlight_class_finalize (EMailDisplayPopupTextHighlightClass *klass) { - } static void e_mail_display_popup_text_highlight_init (EMailDisplayPopupTextHighlight *extension) { extension->action_group = NULL; + extension->document_uri = NULL; } diff --git a/modules/vcard-inline/e-mail-formatter-vcard.c b/modules/vcard-inline/e-mail-formatter-vcard.c index f13d677..40e573c 100644 --- a/modules/vcard-inline/e-mail-formatter-vcard.c +++ b/modules/vcard-inline/e-mail-formatter-vcard.c @@ -146,9 +146,12 @@ mail_formatter_vcard_format (EMailFormatterExtension *extension, str = g_strdup_printf ( "<button type=\"button\" " "name=\"set-display-mode\" " + "id=\"%s\" " "class=\"org-gnome-vcard-display-mode-button\" " "value=\"%d\" " + "style=\"margin-left: 0px\"" "accesskey=\"%s\">%s</button>", + e_mail_part_get_id (part), mode, access_key, html_label); g_output_stream_write_all ( stream, str, strlen (str), NULL, cancellable, NULL); @@ -166,8 +169,10 @@ mail_formatter_vcard_format (EMailFormatterExtension *extension, "class=\"org-gnome-vcard-save-button\" " "value=\"%s\" " "accesskey=\"%s\">%s</button><br>" - "<iframe width=\"100%%\" height=\"auto\" frameborder=\"0\"" - "src=\"%s\" name=\"%s\"></iframe>" + "<iframe width=\"100%%\" height=\"auto\" " + " class=\"-e-mail-formatter-frame-color -e-web-view-background-color\" " + " style=\"border: 1px solid;\"" + " src=\"%s\" name=\"%s\"></iframe>" "</div>", e_mail_part_get_id (part), access_key, html_label, uri, diff --git a/modules/vcard-inline/e-mail-part-vcard.c b/modules/vcard-inline/e-mail-part-vcard.c index f164ab6..f5729a6 100644 --- a/modules/vcard-inline/e-mail-part-vcard.c +++ b/modules/vcard-inline/e-mail-part-vcard.c @@ -32,6 +32,12 @@ struct _EMailPartVCardPrivate { gint placeholder; + + guint display_mode_toggled_signal_id; + guint save_vcard_button_pressed_signal_id; + + GDBusProxy *web_extension; + guint64 page_id; }; G_DEFINE_DYNAMIC_TYPE ( @@ -85,8 +91,12 @@ client_connect_cb (GObject *source_object, } static void -save_vcard_cb (WebKitDOMEventTarget *button, - WebKitDOMEvent *event, +save_vcard_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, EMailPartVCard *vcard_part) { EShell *shell; @@ -94,9 +104,19 @@ save_vcard_cb (WebKitDOMEventTarget *button, ESourceRegistry *registry; ESourceSelector *selector; GSList *contact_list; - const gchar *extension_name; + const gchar *extension_name, *button_value, *part_id; GtkWidget *dialog; + if (g_strcmp0 (signal_name, "VCardInlineSaveButtonPressed") != 0) + return; + + g_variant_get (parameters, "(&s)", &button_value); + + part_id = e_mail_part_get_id (E_MAIL_PART (vcard_part)); + + if (!strstr (part_id, button_value)) + return; + shell = e_shell_get_default (); registry = e_shell_get_registry (shell); extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; @@ -131,8 +151,12 @@ save_vcard_cb (WebKitDOMEventTarget *button, } static void -display_mode_toggle_cb (WebKitDOMEventTarget *button, - WebKitDOMEvent *event, +display_mode_toggle_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, EMailPartVCard *vcard_part) { EABContactDisplayMode mode; @@ -140,47 +164,54 @@ display_mode_toggle_cb (WebKitDOMEventTarget *button, gchar *html_label; gchar *access_key; const gchar *part_id; + const gchar *button_id; + + if (g_strcmp0 (signal_name, "VCardInlineDisplayModeToggled") != 0) + return; + + if (!vcard_part->priv->web_extension) + return; + + g_variant_get (parameters, "(&s)", &button_id); part_id = e_mail_part_get_id (E_MAIL_PART (vcard_part)); + if (!strstr (part_id, button_id)) + return; + mode = eab_contact_formatter_get_display_mode (vcard_part->formatter); if (mode == EAB_CONTACT_DISPLAY_RENDER_NORMAL) { mode = EAB_CONTACT_DISPLAY_RENDER_COMPACT; html_label = e_mail_formatter_parse_html_mnemonics ( _("Show F_ull vCard"), &access_key); - - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (button), - html_label, NULL); - if (access_key) { - webkit_dom_html_element_set_access_key ( - WEBKIT_DOM_HTML_ELEMENT (button), - access_key); - g_free (access_key); - } - - g_free (html_label); - } else { mode = EAB_CONTACT_DISPLAY_RENDER_NORMAL; html_label = e_mail_formatter_parse_html_mnemonics ( _("Show Com_pact vCard"), &access_key); - - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (button), - html_label, NULL); - if (access_key) { - webkit_dom_html_element_set_access_key ( - WEBKIT_DOM_HTML_ELEMENT (button), - access_key); - g_free (access_key); - } - - g_free (html_label); } + g_dbus_proxy_call ( + vcard_part->priv->web_extension, + "VCardInlineUpdateButton", + g_variant_new ( + "(tsss)", + vcard_part->priv->page_id, + button_id, + html_label, + access_key), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + if (access_key) + g_free (access_key); + + g_free (html_label); + eab_contact_formatter_set_display_mode (vcard_part->formatter, mode); uri = e_mail_part_build_uri ( @@ -188,8 +219,19 @@ display_mode_toggle_cb (WebKitDOMEventTarget *button, "part_id", G_TYPE_STRING, part_id, "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, NULL); - webkit_dom_html_iframe_element_set_src ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (vcard_part->iframe), uri); + g_dbus_proxy_call ( + vcard_part->priv->web_extension, + "VCardInlineSetIFrameSrc", + g_variant_new ( + "(tss)", + vcard_part->priv->page_id, + button_id, + uri), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); g_free (uri); } @@ -202,11 +244,22 @@ mail_part_vcard_dispose (GObject *object) g_clear_object (&part->contact_display); g_clear_object (&part->message_label); g_clear_object (&part->formatter); - g_clear_object (&part->iframe); - g_clear_object (&part->save_button); - g_clear_object (&part->toggle_button); g_clear_object (&part->folder); + if (part->priv->display_mode_toggled_signal_id > 0) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection (part->priv->web_extension), + part->priv->display_mode_toggled_signal_id); + part->priv->display_mode_toggled_signal_id = 0; + } + + if (part->priv->save_vcard_button_pressed_signal_id > 0) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection (part->priv->web_extension), + part->priv->save_vcard_button_pressed_signal_id); + part->priv->save_vcard_button_pressed_signal_id = 0; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_mail_part_vcard_parent_class)->dispose (object); } @@ -249,66 +302,55 @@ mail_part_vcard_constructed (GObject *object) static void mail_part_vcard_bind_dom_element (EMailPart *part, - WebKitDOMElement *element) + GDBusProxy *evolution_web_extension, + guint64 page_id, + const gchar *element_id) { EMailPartVCard *vcard_part; - WebKitDOMNodeList *list; - WebKitDOMElement *iframe; - WebKitDOMElement *toggle_button; - WebKitDOMElement *save_button; vcard_part = E_MAIL_PART_VCARD (part); - /* IFRAME */ - list = webkit_dom_element_get_elements_by_tag_name ( - element, "iframe"); - if (webkit_dom_node_list_get_length (list) != 1) { - g_object_unref (list); - return; - } - iframe = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, 0)); - g_clear_object (&vcard_part->iframe); - vcard_part->iframe = iframe; - g_object_unref (list); - - /* TOGGLE DISPLAY MODE BUTTON */ - list = webkit_dom_element_get_elements_by_class_name ( - element, "org-gnome-vcard-display-mode-button"); - if (webkit_dom_node_list_get_length (list) != 1) { - g_object_unref (list); - return; - } - toggle_button = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, 0)); - g_clear_object (&vcard_part->toggle_button); - vcard_part->toggle_button = toggle_button; - g_object_unref (list); - - /* SAVE TO ADDRESSBOOK BUTTON */ - list = webkit_dom_element_get_elements_by_class_name ( - element, "org-gnome-vcard-save-button"); - if (webkit_dom_node_list_get_length (list) != 1) { - g_object_unref (list); - return; - } - save_button = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, 0)); - g_clear_object (&vcard_part->save_button); - vcard_part->save_button = save_button; - g_object_unref (list); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (toggle_button), - "click", G_CALLBACK (display_mode_toggle_cb), - FALSE, vcard_part); - - webkit_dom_event_target_add_event_listener ( - WEBKIT_DOM_EVENT_TARGET (save_button), - "click", G_CALLBACK (save_vcard_cb), - FALSE, vcard_part); - - /* Bind collapse buttons for contact lists. */ - eab_contact_formatter_bind_dom ( - webkit_dom_html_iframe_element_get_content_document ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe))); + vcard_part->priv->web_extension = evolution_web_extension; + vcard_part->priv->page_id = page_id; + + vcard_part->priv->display_mode_toggled_signal_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (evolution_web_extension), + g_dbus_proxy_get_name (evolution_web_extension), + g_dbus_proxy_get_interface_name (evolution_web_extension), + "VCardInlineDisplayModeToggled", + g_dbus_proxy_get_object_path (evolution_web_extension), + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback) display_mode_toggle_cb, + vcard_part, + NULL); + + vcard_part->priv->save_vcard_button_pressed_signal_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (evolution_web_extension), + g_dbus_proxy_get_name (evolution_web_extension), + g_dbus_proxy_get_interface_name (evolution_web_extension), + "VCardInlineSaveButtonPressed", + g_dbus_proxy_get_object_path (evolution_web_extension), + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback) save_vcard_cb, + vcard_part, + NULL); + + g_dbus_proxy_call ( + vcard_part->priv->web_extension, + "VCardInlineBindDOM", + g_variant_new ( + "(ts)", + vcard_part->priv->page_id, + element_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } static void diff --git a/modules/vcard-inline/e-mail-part-vcard.h b/modules/vcard-inline/e-mail-part-vcard.h index d9409c5..9aeb595 100644 --- a/modules/vcard-inline/e-mail-part-vcard.h +++ b/modules/vcard-inline/e-mail-part-vcard.h @@ -21,7 +21,6 @@ #include <em-format/e-mail-part.h> #include <addressbook/gui/widgets/eab-contact-formatter.h> -#include <webkit/webkitdom.h> /* Standard GObject macros */ #define E_TYPE_MAIL_PART_VCARD \ @@ -57,9 +56,6 @@ struct _EMailPartVCard { GtkWidget *message_label; EABContactFormatter *formatter; - WebKitDOMElement *iframe; - WebKitDOMElement *toggle_button; - WebKitDOMElement *save_button; CamelFolder *folder; gchar *message_uid; diff --git a/modules/web-inspector/evolution-web-inspector.c b/modules/web-inspector/evolution-web-inspector.c deleted file mode 100644 index 6ccba80..0000000 --- a/modules/web-inspector/evolution-web-inspector.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * evolution-web-inspector.c - * - * 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. - * - * 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 General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <config.h> -#include <glib/gi18n-lib.h> -#include <gdk/gdkkeysyms.h> - -#include <libebackend/libebackend.h> - -#include <e-util/e-util.h> - -/* Standard GObject macros */ -#define E_TYPE_WEB_INSPECTOR \ - (e_web_inspector_get_type ()) -#define E_WEB_INSPECTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_WEB_INSPECTOR, EWebInspector)) - -/* <Control>+<Shift>+I */ -#define WEB_INSPECTOR_MOD (GDK_CONTROL_MASK | GDK_SHIFT_MASK) -#define WEB_INSPECTOR_KEY (GDK_KEY_I) - -#define WEB_INSPECTOR_SHORTCUT_SHOW(event) \ - ((((event)->state & WEB_INSPECTOR_MOD) == WEB_INSPECTOR_MOD) && \ - ((event)->keyval == WEB_INSPECTOR_KEY)) - -typedef struct _EWebInspector EWebInspector; -typedef struct _EWebInspectorClass EWebInspectorClass; - -struct _EWebInspector { - EExtension parent; -}; - -struct _EWebInspectorClass { - EExtensionClass parent_class; -}; - -/* Module Entry Points */ -void e_module_load (GTypeModule *type_module); -void e_module_unload (GTypeModule *type_module); - -/* Forward Declarations */ -GType e_web_inspector_get_type (void); - -G_DEFINE_DYNAMIC_TYPE (EWebInspector, e_web_inspector, E_TYPE_EXTENSION) - -static WebKitWebView * -web_inspector_get_web_view (EWebInspector *extension) -{ - EExtensible *extensible; - - extensible = e_extension_get_extensible (E_EXTENSION (extension)); - - return WEBKIT_WEB_VIEW (extensible); -} - -static gboolean -web_inspector_key_press_event_cb (WebKitWebView *web_view, - GdkEventKey *event) -{ - WebKitWebInspector *inspector; - gboolean handled = FALSE; - - inspector = webkit_web_view_get_inspector (web_view); - - if (WEB_INSPECTOR_SHORTCUT_SHOW (event)) { - webkit_web_inspector_show (inspector); - handled = TRUE; - } - - return handled; -} - -static WebKitWebView * -web_inspector_inspect_web_view_cb (WebKitWebInspector *inspector) -{ - GtkWidget *web_view; - GtkWidget *window; - const gchar *title; - - title = _("Evolution Web Inspector"); - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_title (GTK_WINDOW (window), title); - gtk_widget_set_size_request (window, 600, 400); - gtk_widget_show (window); - - web_view = webkit_web_view_new (); - gtk_container_add (GTK_CONTAINER (window), web_view); - gtk_widget_show (web_view); - - return WEBKIT_WEB_VIEW (web_view); -} - -static void -web_inspector_constructed (GObject *object) -{ - EWebInspector *extension; - WebKitWebView *web_view; - WebKitWebSettings *settings; - WebKitWebInspector *inspector; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_web_inspector_parent_class)->constructed (object); - - extension = E_WEB_INSPECTOR (object); - web_view = web_inspector_get_web_view (extension); - settings = webkit_web_view_get_settings (web_view); - inspector = webkit_web_view_get_inspector (web_view); - - g_object_set (settings, "enable-developer-extras", TRUE, NULL); - - g_signal_connect ( - web_view, "key-press-event", - G_CALLBACK (web_inspector_key_press_event_cb), NULL); - - g_signal_connect ( - inspector, "inspect-web-view", - G_CALLBACK (web_inspector_inspect_web_view_cb), NULL); -} - -static void -e_web_inspector_class_init (EWebInspectorClass *class) -{ - GObjectClass *object_class; - EExtensionClass *extension_class; - - object_class = G_OBJECT_CLASS (class); - object_class->constructed = web_inspector_constructed; - - extension_class = E_EXTENSION_CLASS (class); - extension_class->extensible_type = WEBKIT_TYPE_WEB_VIEW; -} - -static void -e_web_inspector_class_finalize (EWebInspectorClass *class) -{ -} - -static void -e_web_inspector_init (EWebInspector *extension) -{ -} - -G_MODULE_EXPORT void -e_module_load (GTypeModule *type_module) -{ - e_web_inspector_register_type (type_module); -} - -G_MODULE_EXPORT void -e_module_unload (GTypeModule *type_module) -{ -} diff --git a/modules/webkit-editor/Makefile.am b/modules/webkit-editor/Makefile.am new file mode 100644 index 0000000..1844797 --- /dev/null +++ b/modules/webkit-editor/Makefile.am @@ -0,0 +1,31 @@ +SUBDIRS = . web-extension + +module_LTLIBRARIES = module-webkit-editor.la + +module_webkit_editor_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -DEVOLUTION_WEB_EXTENSIONS_WEBKIT_EDITOR_DIR=\""$(webextensionswebkiteditordir)"\" \ + -DG_LOG_DOMAIN=\"webkit-editor\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(CODE_COVERAGE_CFLAGS) \ + $(NULL) + +module_webkit_editor_la_SOURCES = \ + evolution-module-webkit-editor.c \ + e-webkit-editor-extension.c \ + e-webkit-editor-extension.h \ + e-webkit-editor.c \ + e-webkit-editor.h + +module_webkit_editor_la_LIBADD = \ + $(top_builddir)/e-util/libevolution-util.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(NULL) + +module_webkit_editor_la_LDFLAGS = \ + -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS) + +-include $(top_srcdir)/git.mk diff --git a/modules/webkit-editor/e-webkit-editor-extension.c b/modules/webkit-editor/e-webkit-editor-extension.c new file mode 100644 index 0000000..e05ee20 --- /dev/null +++ b/modules/webkit-editor/e-webkit-editor-extension.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-webkit-editor-extension.h" +#include "e-webkit-editor.h" + +#include <e-util/e-util.h> + +#define E_WEBKIT_EDITOR_EXTENSION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_WEBKIT_EDITOR_EXTENSION, EWebKitEditorExtensionPrivate)) + +struct _EWebKitEditorExtensionPrivate { + EWebKitEditor *wk_editor; +}; + +G_DEFINE_DYNAMIC_TYPE ( + EWebKitEditorExtension, + e_webkit_editor_extension, + E_TYPE_EXTENSION) + +static void +e_webkit_editor_extension_init (EWebKitEditorExtension *editor_extension) +{ + editor_extension->priv = E_WEBKIT_EDITOR_EXTENSION_GET_PRIVATE (editor_extension); + + editor_extension->priv->wk_editor = g_object_ref_sink (e_webkit_editor_new ()); +} + +static void +webkit_editor_extension_constructed (GObject *object) +{ + EWebKitEditorExtensionPrivate *priv; + EExtensible *extensible; + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_webkit_editor_extension_parent_class)->constructed (object); + + priv = E_WEBKIT_EDITOR_EXTENSION_GET_PRIVATE (object); + extensible = e_extension_get_extensible (E_EXTENSION (object)); + + e_html_editor_register_content_editor (E_HTML_EDITOR (extensible), + DEFAULT_CONTENT_EDITOR_NAME, E_CONTENT_EDITOR (priv->wk_editor)); +} + +static void +webkit_editor_extension_dispose (GObject *object) +{ + EWebKitEditorExtensionPrivate *priv; + + priv = E_WEBKIT_EDITOR_EXTENSION_GET_PRIVATE (object); + + g_clear_object (&priv->wk_editor); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_webkit_editor_extension_parent_class)->dispose (object); +} + +static void +e_webkit_editor_extension_class_init (EWebKitEditorExtensionClass *class) +{ + EExtensionClass *extension_class; + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EWebKitEditorExtensionPrivate)); + + extension_class = E_EXTENSION_CLASS (class); + extension_class->extensible_type = E_TYPE_HTML_EDITOR; + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = webkit_editor_extension_dispose; + object_class->constructed = webkit_editor_extension_constructed; +} + +static void +e_webkit_editor_extension_class_finalize (EWebKitEditorExtensionClass *class) +{ +} + +void +e_webkit_editor_extension_type_register (GTypeModule *type_module) +{ + e_webkit_editor_extension_register_type (type_module); +} diff --git a/modules/webkit-editor/e-webkit-editor-extension.h b/modules/webkit-editor/e-webkit-editor-extension.h new file mode 100644 index 0000000..ff5d781 --- /dev/null +++ b/modules/webkit-editor/e-webkit-editor-extension.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef E_WEBKIT_EDITOR_EXTENSION_H +#define E_WEBKIT_EDITOR_EXTENSION_H + +#include <libebackend/libebackend.h> + +/* Standard GObject macros */ +#define E_TYPE_WEBKIT_EDITOR_EXTENSION \ + (e_webkit_editor_extension_get_type ()) +#define E_WEBKIT_EDITOR_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_WEBKIT_EDITOR_EXTENSION, EWebKitEditorExtension)) +#define E_WEBKIT_EDITOR_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_WEBKIT_EDITOR_EXTENSION, EWebKitEditorExtensionClass)) +#define E_IS_WEBKIT_EDITOR_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_WEBKIT_EDITOR_EXTENSION)) +#define E_IS_WEBKIT_EDITOR_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_WEBKIT_EDITOR_EXTENSION)) +#define E_WEBKIT_EDITOR_EXTENSION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_WEBKIT_EDITOR_EXTENSION, EWebKitEditorExtensionClass)) + +G_BEGIN_DECLS + +typedef struct _EWebKitEditorExtension EWebKitEditorExtension; +typedef struct _EWebKitEditorExtensionClass EWebKitEditorExtensionClass; +typedef struct _EWebKitEditorExtensionPrivate EWebKitEditorExtensionPrivate; + +struct _EWebKitEditorExtension { + EExtension parent; + + EWebKitEditorExtensionPrivate *priv; +}; + +struct _EWebKitEditorExtensionClass { + EExtensionClass parent_class; +}; + +GType e_webkit_editor_extension_get_type (void) G_GNUC_CONST; +void e_webkit_editor_extension_type_register (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_WEBKIT_EDITOR_EXTENSION_H */ diff --git a/modules/webkit-editor/e-webkit-editor.c b/modules/webkit-editor/e-webkit-editor.c new file mode 100644 index 0000000..c984a57 --- /dev/null +++ b/modules/webkit-editor/e-webkit-editor.c @@ -0,0 +1,6291 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-webkit-editor.h" + +#include "web-extension/e-editor-web-extension-names.h" + +#include <e-util/e-util.h> +#include <string.h> + +#define E_WEBKIT_EDITOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_WEBKIT_EDITOR, EWebKitEditorPrivate)) + +/* FIXME WK2 Move to e-content-editor? */ +#define UNICODE_NBSP "\xc2\xa0" +#define SPACES_PER_LIST_LEVEL 3 +#define SPACES_ORDERED_LIST_FIRST_LEVEL 6 + +enum { + PROP_0, + PROP_WEB_EXTENSION, /* for test purposes */ + PROP_CAN_COPY, + PROP_CAN_CUT, + PROP_CAN_PASTE, + PROP_CAN_REDO, + PROP_CAN_UNDO, + PROP_CHANGED, + PROP_EDITABLE, + PROP_HTML_MODE, + PROP_SPELL_CHECK_ENABLED, + PROP_SPELL_CHECKER, + + PROP_ALIGNMENT, + PROP_BACKGROUND_COLOR, + PROP_BLOCK_FORMAT, + PROP_BOLD, + PROP_FONT_COLOR, + PROP_FONT_NAME, + PROP_FONT_SIZE, + PROP_INDENTED, + PROP_ITALIC, + PROP_MONOSPACED, + PROP_STRIKETHROUGH, + PROP_SUBSCRIPT, + PROP_SUPERSCRIPT, + PROP_UNDERLINE +}; + +struct _EWebKitEditorPrivate { + EContentEditorInitializedCallback initialized_callback; + gpointer initialized_user_data; + + GDBusProxy *web_extension; + guint web_extension_watch_name_id; + guint web_extension_selection_changed_cb_id; + guint web_extension_content_changed_cb_id; + guint web_extension_undo_redo_state_changed_cb_id; + + gboolean html_mode; + gboolean changed; + gboolean can_copy; + gboolean can_cut; + gboolean can_paste; + gboolean can_undo; + gboolean can_redo; + + gboolean emit_load_finished_when_extension_is_ready; + gboolean reload_in_progress; + gboolean copy_paste_clipboard_in_view; + gboolean copy_paste_primary_in_view; + gboolean copy_cut_actions_triggered; + gboolean pasting_primary_clipboard; + gboolean pasting_from_itself_extension_value; + + guint32 style_flags; + gboolean is_indented; + + GdkRGBA *background_color; + GdkRGBA *font_color; + + gchar *font_name; + + guint font_size; + + EContentEditorBlockFormat block_format; + EContentEditorAlignment alignment; + + gchar *current_user_stylesheet; + + WebKitLoadEvent webkit_load_event; + + GQueue *post_reload_operations; + + GSettings *mail_settings; + GSettings *font_settings; + GSettings *aliasing_settings; + + GHashTable *old_settings; + + ESpellChecker *spell_checker; + gboolean spell_check_enabled; + + gulong owner_change_primary_clipboard_cb_id; + gulong owner_change_clipboard_cb_id; + + WebKitFindController *find_controller; /* not referenced; set to non-NULL only if the search is in progress */ + gboolean performing_replace_all; + guint replaced_count; + gchar *replace_with; + gulong found_text_handler_id; + gulong failed_to_find_text_handler_id; +}; + +static const GdkRGBA black = { 0, 0, 0, 1 }; +static const GdkRGBA white = { 1, 1, 1, 1 }; +static const GdkRGBA transparent = { 0, 0, 0, 0 }; + +typedef void (*PostReloadOperationFunc) (EWebKitEditor *wk_editor, gpointer data, EContentEditorInsertContentFlags flags); + +typedef struct { + PostReloadOperationFunc func; + EContentEditorInsertContentFlags flags; + gpointer data; + GDestroyNotify data_free_func; +} PostReloadOperation; + +static void e_webkit_editor_content_editor_init (EContentEditorInterface *iface); + +G_DEFINE_TYPE_WITH_CODE ( + EWebKitEditor, + e_webkit_editor, + WEBKIT_TYPE_WEB_VIEW, + G_IMPLEMENT_INTERFACE ( + E_TYPE_CONTENT_EDITOR, + e_webkit_editor_content_editor_init)); + +EWebKitEditor * +e_webkit_editor_new (void) +{ + return g_object_new (E_TYPE_WEBKIT_EDITOR, NULL); +} + +static void +webkit_editor_can_paste_cb (WebKitWebView *view, + GAsyncResult *result, + EWebKitEditor *wk_editor) +{ + gboolean value; + + value = webkit_web_view_can_execute_editing_command_finish (view, result, NULL); + + if (wk_editor->priv->can_paste != value) { + wk_editor->priv->can_paste = value; + g_object_notify (G_OBJECT (wk_editor), "can-paste"); + } +} + +static gboolean +webkit_editor_can_paste (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return wk_editor->priv->can_paste; +} + +static void +webkit_editor_can_cut_cb (WebKitWebView *view, + GAsyncResult *result, + EWebKitEditor *wk_editor) +{ + gboolean value; + + value = webkit_web_view_can_execute_editing_command_finish (view, result, NULL); + + if (wk_editor->priv->can_cut != value) { + wk_editor->priv->can_cut = value; + g_object_notify (G_OBJECT (wk_editor), "can-cut"); + } +} + +static gboolean +webkit_editor_can_cut (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return wk_editor->priv->can_cut; +} + +static void +webkit_editor_can_copy_cb (WebKitWebView *view, + GAsyncResult *result, + EWebKitEditor *wk_editor) +{ + gboolean value; + + value = webkit_web_view_can_execute_editing_command_finish (view, result, NULL); + + if (wk_editor->priv->can_copy != value) { + wk_editor->priv->can_copy = value; + /* This means that we have an active selection thus the primary + * clipboard content is from composer. */ + if (value) + wk_editor->priv->copy_paste_primary_in_view = TRUE; + /* FIXME notify web extension about pasting content from itself */ + g_object_notify (G_OBJECT (wk_editor), "can-copy"); + } +} + +static gboolean +webkit_editor_can_copy (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return wk_editor->priv->can_copy; +} + +static gboolean +webkit_editor_get_changed (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return wk_editor->priv->changed; +} + +static void +webkit_editor_set_changed (EWebKitEditor *wk_editor, + gboolean changed) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if (wk_editor->priv->changed == changed) + return; + + wk_editor->priv->changed = changed; + + g_object_notify (G_OBJECT (wk_editor), "changed"); +} + +static void +webkit_editor_set_can_undo (EWebKitEditor *wk_editor, + gboolean can_undo) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if ((wk_editor->priv->can_undo ? 1 : 0) == (can_undo ? 1 : 0)) + return; + + wk_editor->priv->can_undo = can_undo; + + g_object_notify (G_OBJECT (wk_editor), "can-undo"); +} + +static void +webkit_editor_set_can_redo (EWebKitEditor *wk_editor, + gboolean can_redo) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if ((wk_editor->priv->can_redo ? 1 : 0) == (can_redo ? 1 : 0)) + return; + + wk_editor->priv->can_redo = can_redo; + + g_object_notify (G_OBJECT (wk_editor), "can-redo"); +} + +static void +web_extension_content_changed_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + EWebKitEditor *wk_editor) +{ + if (g_strcmp0 (signal_name, "ContentChanged") != 0) + return; + + if (parameters) { + guint64 page_id = 0; + + g_variant_get (parameters, "(t)", &page_id); + + if (page_id == webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor))) + webkit_editor_set_changed (wk_editor, TRUE); + } +} + +static void +web_extension_selection_changed_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + EWebKitEditor *wk_editor) +{ + guint64 page_id = 0; + gchar *font_color = NULL; + guint32 alignment, block_format, style_flags, font_size; + gboolean is_indented; + + if (g_strcmp0 (signal_name, "SelectionChanged") != 0) + return; + + if (!parameters) + return; + + g_variant_get ( + parameters, + "(tiibiis)", + &page_id, + &alignment, + &block_format, + &is_indented, + &style_flags, + &font_size, + &font_color); + + if (page_id != webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor))) { + g_free (font_color); + return; + } + + webkit_web_view_can_execute_editing_command ( + WEBKIT_WEB_VIEW (wk_editor), + WEBKIT_EDITING_COMMAND_COPY, + NULL, /* cancellable */ + (GAsyncReadyCallback) webkit_editor_can_copy_cb, + wk_editor); + + webkit_web_view_can_execute_editing_command ( + WEBKIT_WEB_VIEW (wk_editor), + WEBKIT_EDITING_COMMAND_CUT, + NULL, /* cancellable */ + (GAsyncReadyCallback) webkit_editor_can_cut_cb, + wk_editor); + + webkit_web_view_can_execute_editing_command ( + WEBKIT_WEB_VIEW (wk_editor), + WEBKIT_EDITING_COMMAND_PASTE, + NULL, /* cancellable */ + (GAsyncReadyCallback) webkit_editor_can_paste_cb, + wk_editor); + + g_object_freeze_notify (G_OBJECT (wk_editor)); + + wk_editor->priv->alignment = alignment; + wk_editor->priv->block_format = block_format; + wk_editor->priv->is_indented = is_indented; + wk_editor->priv->style_flags = style_flags; + wk_editor->priv->font_size = font_size; + + if (wk_editor->priv->html_mode) { + GdkRGBA color; + + if (font_color && *font_color && gdk_rgba_parse (&color, font_color)) { + if (wk_editor->priv->font_color) + gdk_rgba_free (wk_editor->priv->font_color); + wk_editor->priv->font_color = gdk_rgba_copy (&color); + } + } + g_free (font_color); + + g_object_notify (G_OBJECT (wk_editor), "alignment"); + g_object_notify (G_OBJECT (wk_editor), "block-format"); + g_object_notify (G_OBJECT (wk_editor), "indented"); + + if (wk_editor->priv->html_mode) { + /* g_object_notify (G_OBJECT (wk_editor), "background-color"); */ + g_object_notify (G_OBJECT (wk_editor), "bold"); + /* g_object_notify (G_OBJECT (wk_editor), "font-name"); */ + g_object_notify (G_OBJECT (wk_editor), "font-size"); + g_object_notify (G_OBJECT (wk_editor), "font-color"); + g_object_notify (G_OBJECT (wk_editor), "italic"); + g_object_notify (G_OBJECT (wk_editor), "monospaced"); + g_object_notify (G_OBJECT (wk_editor), "strikethrough"); + g_object_notify (G_OBJECT (wk_editor), "subscript"); + g_object_notify (G_OBJECT (wk_editor), "superscript"); + g_object_notify (G_OBJECT (wk_editor), "underline"); + } + + g_object_thaw_notify (G_OBJECT (wk_editor)); +} + +static void +web_extension_undo_redo_state_changed_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + EWebKitEditor *wk_editor) +{ + guint64 page_id = 0; + gboolean can_undo = FALSE, can_redo = FALSE; + + if (g_strcmp0 (signal_name, "UndoRedoStateChanged") != 0) + return; + + g_variant_get (parameters, "(tbb)", &page_id, &can_undo, &can_redo); + + if (page_id == webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor))) { + webkit_editor_set_can_undo (wk_editor, can_undo); + webkit_editor_set_can_redo (wk_editor, can_redo); + } +} + +static void +dispatch_pending_operations (EWebKitEditor *wk_editor) +{ + if (wk_editor->priv->webkit_load_event != WEBKIT_LOAD_FINISHED || + !wk_editor->priv->web_extension) + return; + + /* Dispatch queued operations - as we are using this just for load + * operations load just the latest request and throw away the rest. */ + if (wk_editor->priv->post_reload_operations && + !g_queue_is_empty (wk_editor->priv->post_reload_operations)) { + + PostReloadOperation *op; + + op = g_queue_pop_head (wk_editor->priv->post_reload_operations); + + op->func (wk_editor, op->data, op->flags); + + if (op->data_free_func) + op->data_free_func (op->data); + g_free (op); + + while ((op = g_queue_pop_head (wk_editor->priv->post_reload_operations))) { + if (op->data_free_func) + op->data_free_func (op->data); + g_free (op); + } + + g_queue_clear (wk_editor->priv->post_reload_operations); + } +} + +static void +web_extension_proxy_created_cb (GDBusProxy *proxy, + GAsyncResult *result, + EWebKitEditor *wk_editor) +{ + GError *error = NULL; + + wk_editor->priv->web_extension = g_dbus_proxy_new_finish (result, &error); + if (!wk_editor->priv->web_extension) { + g_warning ("Error creating web extension proxy: %s\n", error->message); + g_error_free (error); + + if (wk_editor->priv->initialized_callback) { + wk_editor->priv->initialized_callback (E_CONTENT_EDITOR (wk_editor), wk_editor->priv->initialized_user_data); + + wk_editor->priv->initialized_callback = NULL; + wk_editor->priv->initialized_user_data = NULL; + } + + return; + } + + if (wk_editor->priv->web_extension_selection_changed_cb_id == 0) { + wk_editor->priv->web_extension_selection_changed_cb_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (wk_editor->priv->web_extension), + g_dbus_proxy_get_name (wk_editor->priv->web_extension), + E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE, + "SelectionChanged", + E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback) web_extension_selection_changed_cb, + wk_editor, + NULL); + } + + if (wk_editor->priv->web_extension_content_changed_cb_id == 0) { + wk_editor->priv->web_extension_content_changed_cb_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (wk_editor->priv->web_extension), + g_dbus_proxy_get_name (wk_editor->priv->web_extension), + E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE, + "ContentChanged", + E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback) web_extension_content_changed_cb, + wk_editor, + NULL); + } + + if (wk_editor->priv->web_extension_undo_redo_state_changed_cb_id == 0) { + wk_editor->priv->web_extension_undo_redo_state_changed_cb_id = + g_dbus_connection_signal_subscribe ( + g_dbus_proxy_get_connection (wk_editor->priv->web_extension), + g_dbus_proxy_get_name (wk_editor->priv->web_extension), + E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE, + "UndoRedoStateChanged", + E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback) web_extension_undo_redo_state_changed_cb, + wk_editor, + NULL); + } + + dispatch_pending_operations (wk_editor); + + if (wk_editor->priv->emit_load_finished_when_extension_is_ready) { + e_content_editor_emit_load_finished (E_CONTENT_EDITOR (wk_editor)); + + wk_editor->priv->emit_load_finished_when_extension_is_ready = FALSE; + } + + g_object_notify (G_OBJECT (wk_editor), "web-extension"); + + if (wk_editor->priv->initialized_callback) { + wk_editor->priv->initialized_callback (E_CONTENT_EDITOR (wk_editor), wk_editor->priv->initialized_user_data); + + wk_editor->priv->initialized_callback = NULL; + wk_editor->priv->initialized_user_data = NULL; + } +} + +static void +web_extension_appeared_cb (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + EWebKitEditor *wk_editor) +{ + g_dbus_proxy_new ( + connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + name, + E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, + E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE, + NULL, + (GAsyncReadyCallback) web_extension_proxy_created_cb, + wk_editor); +} + +static void +web_extension_vanished_cb (GDBusConnection *connection, + const gchar *name, + EWebKitEditor *wk_editor) +{ + g_clear_object (&wk_editor->priv->web_extension); +} + +static void +webkit_editor_watch_web_extension (EWebKitEditor *wk_editor) +{ + wk_editor->priv->web_extension_watch_name_id = + g_bus_watch_name ( + G_BUS_TYPE_SESSION, + E_WEBKIT_EDITOR_WEB_EXTENSION_SERVICE_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + (GBusNameAppearedCallback) web_extension_appeared_cb, + (GBusNameVanishedCallback) web_extension_vanished_cb, + wk_editor, + NULL); +} + +static guint64 +current_page_id (EWebKitEditor *wk_editor) +{ + return webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor)); +} + +static void +sync_wrapper_result_callback (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GAsyncResult **out_async_result = user_data; + + g_return_if_fail (out_async_result != NULL); + g_return_if_fail (*out_async_result == NULL); + + *out_async_result = g_object_ref (result); +} + +/* Wraps GDBusProxy synchronous call into an asynchronous without blocking + the main context, thus there is no freeze when this is called in the UI + process and the WebProcess also does its own IPC call. */ +static GVariant * +g_dbus_proxy_call_sync_wrapper (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error) +{ + GAsyncResult *async_result = NULL; + GVariant *var_result; + + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + g_return_val_if_fail (method_name != NULL, NULL); + + g_dbus_proxy_call ( + proxy, method_name, parameters, flags, timeout_msec, cancellable, + sync_wrapper_result_callback, &async_result); + + while (!async_result) { + g_main_context_iteration (NULL, TRUE); + } + + var_result = g_dbus_proxy_call_finish (proxy, async_result, error); + + g_clear_object (&async_result); + + return var_result; +} + +static void +webkit_editor_call_simple_extension_function_sync (EWebKitEditor *wk_editor, + const gchar *function) +{ + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + function, + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); +} + +static void +webkit_editor_call_simple_extension_function (EWebKitEditor *wk_editor, + const gchar *function) +{ + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + function, + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static GVariant * +webkit_editor_get_element_attribute (EWebKitEditor *wk_editor, + const gchar *selector, + const gchar *attribute) +{ + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return NULL; + } + + return g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ElementGetAttributeBySelector", + g_variant_new ("(tss)", current_page_id (wk_editor), selector, attribute), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); +} + +static void +webkit_editor_set_element_attribute (EWebKitEditor *wk_editor, + const gchar *selector, + const gchar *attribute, + const gchar *value) +{ + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "ElementSetAttributeBySelector", + g_variant_new ( + "(tsss)", current_page_id (wk_editor), selector, attribute, value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_remove_element_attribute (EWebKitEditor *wk_editor, + const gchar *selector, + const gchar *attribute) +{ + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "ElementRemoveAttributeBySelector", + g_variant_new ("(tss)", current_page_id (wk_editor), selector, attribute), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_set_format_boolean (EWebKitEditor *wk_editor, + const gchar *format_dom_function, + gboolean format_value) +{ + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + format_dom_function, + g_variant_new ("(tb)", current_page_id (wk_editor), format_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_set_format_int (EWebKitEditor *wk_editor, + const gchar *format_dom_function, + gint32 format_value) +{ + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + format_dom_function, + g_variant_new ("(ti)", current_page_id (wk_editor), format_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_set_format_string (EWebKitEditor *wk_editor, + const gchar *format_name, + const gchar *format_dom_function, + const gchar *format_value) +{ + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (!wk_editor->priv->html_mode) + return; + + webkit_editor_set_changed (wk_editor, TRUE); + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + format_dom_function, + g_variant_new ("(ts)", current_page_id (wk_editor), format_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + g_object_notify (G_OBJECT (wk_editor), format_name); +} + +static void +webkit_editor_queue_post_reload_operation (EWebKitEditor *wk_editor, + PostReloadOperationFunc func, + gpointer data, + GDestroyNotify data_free_func, + EContentEditorInsertContentFlags flags) +{ + PostReloadOperation *op; + + g_return_if_fail (func != NULL); + + if (wk_editor->priv->post_reload_operations == NULL) + wk_editor->priv->post_reload_operations = g_queue_new (); + + op = g_new0 (PostReloadOperation, 1); + op->func = func; + op->flags = flags; + op->data = data; + op->data_free_func = data_free_func; + + g_queue_push_head (wk_editor->priv->post_reload_operations, op); +} + +static void +webkit_editor_show_inspector (EWebKitEditor *wk_editor) +{ + WebKitWebInspector *inspector; + + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (wk_editor)); + + webkit_web_inspector_show (inspector); +} + +static void +webkit_editor_initialize (EContentEditor *content_editor, + EContentEditorInitializedCallback callback, + gpointer user_data) +{ + EWebKitEditor *wk_editor; + + g_return_if_fail (E_IS_WEBKIT_EDITOR (content_editor)); + g_return_if_fail (callback != NULL); + + wk_editor = E_WEBKIT_EDITOR (content_editor); + + if (wk_editor->priv->web_extension) { + callback (content_editor, user_data); + } else { + g_return_if_fail (wk_editor->priv->initialized_callback == NULL); + + wk_editor->priv->initialized_callback = callback; + wk_editor->priv->initialized_user_data = user_data; + } +} + +static void +webkit_editor_update_styles (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gboolean mark_citations, use_custom_font; + gchar *font, *aa = NULL, *citation_color; + const gchar *styles[] = { "normal", "oblique", "italic" }; + const gchar *smoothing = NULL; + GString *stylesheet; + PangoFontDescription *min_size, *ms, *vw; + WebKitSettings *settings; + WebKitUserContentManager *manager; + WebKitUserStyleSheet *style_sheet; + + wk_editor = E_WEBKIT_EDITOR (editor); + + use_custom_font = g_settings_get_boolean ( + wk_editor->priv->mail_settings, "use-custom-font"); + + if (use_custom_font) { + font = g_settings_get_string ( + wk_editor->priv->mail_settings, "monospace-font"); + ms = pango_font_description_from_string (font ? font : "monospace 10"); + g_free (font); + } else { + font = g_settings_get_string ( + wk_editor->priv->font_settings, "monospace-font-name"); + ms = pango_font_description_from_string (font ? font : "monospace 10"); + g_free (font); + } + + if (wk_editor->priv->html_mode) { + if (use_custom_font) { + font = g_settings_get_string ( + wk_editor->priv->mail_settings, "variable-width-font"); + vw = pango_font_description_from_string (font ? font : "serif 10"); + g_free (font); + } else { + font = g_settings_get_string ( + wk_editor->priv->font_settings, "font-name"); + vw = pango_font_description_from_string (font ? font : "serif 10"); + g_free (font); + } + } else { + /* When in plain text mode, force monospace font */ + vw = pango_font_description_copy (ms); + } + + stylesheet = g_string_new (""); + g_string_append_printf ( + stylesheet, + "body {\n" + " font-family: '%s';\n" + " font-size: %dpt;\n" + " font-weight: %d;\n" + " font-style: %s;\n" + " -webkit-line-break: after-white-space;\n", + pango_font_description_get_family (vw), + pango_font_description_get_size (vw) / PANGO_SCALE, + pango_font_description_get_weight (vw), + styles[pango_font_description_get_style (vw)]); + + if (wk_editor->priv->aliasing_settings != NULL) + aa = g_settings_get_string ( + wk_editor->priv->aliasing_settings, "antialiasing"); + + if (g_strcmp0 (aa, "none") == 0) + smoothing = "none"; + else if (g_strcmp0 (aa, "grayscale") == 0) + smoothing = "antialiased"; + else if (g_strcmp0 (aa, "rgba") == 0) + smoothing = "subpixel-antialiased"; + + if (smoothing != NULL) + g_string_append_printf ( + stylesheet, + " -webkit-font-smoothing: %s;\n", + smoothing); + + g_free (aa); + + g_string_append (stylesheet, "}\n"); + + g_string_append_printf ( + stylesheet, + "pre,code,.pre {\n" + " font-family: '%s';\n" + " font-size: %dpt;\n" + " font-weight: %d;\n" + " font-style: %s;\n" + "}", + pango_font_description_get_family (ms), + pango_font_description_get_size (ms) / PANGO_SCALE, + pango_font_description_get_weight (ms), + styles[pango_font_description_get_style (ms)]); + + /* See bug #689777 for details */ + g_string_append ( + stylesheet, + "p,pre,code,address {\n" + " margin: 0;\n" + "}\n" + "h1,h2,h3,h4,h5,h6 {\n" + " margin-top: 0.2em;\n" + " margin-bottom: 0.2em;\n" + "}\n"); + + /* When inserting a table into contenteditable element the width of the + * cells is nearly zero and the td { min-height } doesn't work so put + * unicode zero width space before each cell. */ + g_string_append ( + stylesheet, + "td:before {\n" + " content: \"\xe2\x80\x8b\";" + "}\n"); + + g_string_append ( + stylesheet, + "img " + "{\n" + " height: inherit; \n" + " width: inherit; \n" + "}\n"); + + g_string_append ( + stylesheet, + "span.-x-evo-resizable-wrapper:hover " + "{\n" + " outline: 1px dashed red; \n" + " resize: both; \n" + " overflow: hidden; \n" + " display: inline-block; \n" + "}\n"); + + g_string_append ( + stylesheet, + "td:hover " + "{\n" + " outline: 1px dotted red;\n" + "}\n"); + + g_string_append ( + stylesheet, + "body[data-evo-plain-text] img.-x-evo-smiley-img, " + "body:not([data-evo-plain-text]) span.-x-evo-smiley-text " + "{\n" + " display: none \n" + "}\n"); + + g_string_append ( + stylesheet, + "body[data-evo-plain-text] [data-evo-paragraph] " + "{\n" + " word-wrap: break-word; \n" + " word-break: break-word; \n" + "}\n"); + + g_string_append_printf ( + stylesheet, + ".-x-evo-plaintext-table " + "{\n" + " border-collapse: collapse;\n" + " width: %dch;\n" + "}\n", + g_settings_get_int (wk_editor->priv->mail_settings, "composer-word-wrap-length")); + + g_string_append ( + stylesheet, + ".-x-evo-plaintext-table td " + "{\n" + " vertical-align: top;\n" + "}\n"); + + g_string_append ( + stylesheet, + "td > * " + "{\n" + " display : inline-block;\n" + "}\n"); + + g_string_append_printf ( + stylesheet, + "body[data-evo-plain-text] ul " + "{\n" + " list-style: outside none;\n" + " -webkit-padding-start: %dch; \n" + "}\n", SPACES_PER_LIST_LEVEL); + + g_string_append_printf ( + stylesheet, + "body[data-evo-plain-text] ul > li " + "{\n" + " list-style-position: outside;\n" + " text-indent: -%dch;\n" + "}\n", SPACES_PER_LIST_LEVEL - 1); + + g_string_append ( + stylesheet, + "body[data-evo-plain-text] ul > li::before " + "{\n" + " content: \"*"UNICODE_NBSP"\";\n" + "}\n"); + + g_string_append_printf ( + stylesheet, + "body[data-evo-plain-text] ul.-x-evo-indented " + "{\n" + " -webkit-padding-start: %dch; \n" + "}\n", SPACES_PER_LIST_LEVEL); + + g_string_append ( + stylesheet, + "body:not([data-evo-plain-text]) ul > li.-x-evo-align-center,ol > li.-x-evo-align-center " + "{\n" + " list-style-position: inside;\n" + "}\n"); + + g_string_append ( + stylesheet, + "body:not([data-evo-plain-text]) ul > li.-x-evo-align-right, ol > li.-x-evo-align-right " + "{\n" + " list-style-position: inside;\n" + "}\n"); + + g_string_append_printf ( + stylesheet, + "ol " + "{\n" + " -webkit-padding-start: %dch; \n" + "}\n", SPACES_ORDERED_LIST_FIRST_LEVEL); + + g_string_append_printf ( + stylesheet, + "ol.-x-evo-indented " + "{\n" + " -webkit-padding-start: %dch; \n" + "}\n", SPACES_PER_LIST_LEVEL); + + g_string_append ( + stylesheet, + ".-x-evo-align-left " + "{\n" + " text-align: left; \n" + "}\n"); + + g_string_append ( + stylesheet, + ".-x-evo-align-center " + "{\n" + " text-align: center; \n" + "}\n"); + + g_string_append ( + stylesheet, + ".-x-evo-align-right " + "{\n" + " text-align: right; \n" + "}\n"); + + g_string_append ( + stylesheet, + "ol,ul " + "{\n" + " -webkit-margin-before: 0em; \n" + " -webkit-margin-after: 0em; \n" + "}\n"); + + g_string_append ( + stylesheet, + "blockquote " + "{\n" + " -webkit-margin-before: 0em; \n" + " -webkit-margin-after: 0em; \n" + "}\n"); + + g_string_append ( + stylesheet, + "a " + "{\n" + " word-wrap: break-word; \n" + " word-break: break-all; \n" + "}\n"); + + citation_color = g_settings_get_string ( + wk_editor->priv->mail_settings, "citation-color"); + mark_citations = g_settings_get_boolean ( + wk_editor->priv->mail_settings, "mark-citations"); + + g_string_append ( + stylesheet, + "blockquote[type=cite] " + "{\n" + " padding: 0.0ex 0ex;\n" + " margin: 0ex;\n" + " -webkit-margin-start: 0em; \n" + " -webkit-margin-end : 0em; \n"); + + if (mark_citations && citation_color) + g_string_append_printf ( + stylesheet, + " color: %s !important; \n", + citation_color); + + g_free (citation_color); + citation_color = NULL; + + g_string_append (stylesheet, "}\n"); + + g_string_append_printf ( + stylesheet, + ".-x-evo-quote-character " + "{\n" + " color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (1)); + + g_string_append_printf ( + stylesheet, + ".-x-evo-quote-character+" + ".-x-evo-quote-character" + "{\n" + " color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (2)); + + g_string_append_printf ( + stylesheet, + ".-x-evo-quote-character+" + ".-x-evo-quote-character+" + ".-x-evo-quote-character" + "{\n" + " color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (3)); + + g_string_append_printf ( + stylesheet, + ".-x-evo-quote-character+" + ".-x-evo-quote-character+" + ".-x-evo-quote-character+" + ".-x-evo-quote-character" + "{\n" + " color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (4)); + + g_string_append_printf ( + stylesheet, + ".-x-evo-quote-character+" + ".-x-evo-quote-character+" + ".-x-evo-quote-character+" + ".-x-evo-quote-character+" + ".-x-evo-quote-character" + "{\n" + " color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (5)); + + g_string_append ( + stylesheet, + "body:not([data-evo-plain-text]) " + "blockquote[type=cite] " + "{\n" + " padding: 0ch 1ch 0ch 1ch;\n" + " margin: 0ch;\n" + " border-width: 0px 2px 0px 2px;\n" + " border-style: none solid none solid;\n" + " border-radius: 2px;\n" + "}\n"); + + g_string_append_printf ( + stylesheet, + "body:not([data-evo-plain-text]) " + "blockquote[type=cite] " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (1)); + + g_string_append_printf ( + stylesheet, + "body:not([data-evo-plain-text]) " + "blockquote[type=cite] " + "blockquote[type=cite] " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (2)); + + g_string_append_printf ( + stylesheet, + "body:not([data-evo-plain-text]) " + "blockquote[type=cite] " + "blockquote[type=cite] " + "blockquote[type=cite] " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (3)); + + g_string_append_printf ( + stylesheet, + "body:not([data-evo-plain-text]) " + "blockquote[type=cite] " + "blockquote[type=cite] " + "blockquote[type=cite] " + "blockquote[type=cite] " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (4)); + + g_string_append_printf ( + stylesheet, + "body:not([data-evo-plain-text]) " + "blockquote[type=cite] " + "blockquote[type=cite] " + "blockquote[type=cite] " + "blockquote[type=cite] " + "blockquote[type=cite] " + "{\n" + " border-color: %s;\n" + "}\n", + e_web_view_get_citation_color_for_level (5)); + + if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw) || !wk_editor->priv->html_mode) + min_size = ms; + else + min_size = vw; + + settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (wk_editor)); + g_object_set ( + G_OBJECT (settings), + "default-font-size", + e_util_normalize_font_size ( + GTK_WIDGET (wk_editor), pango_font_description_get_size (vw) / PANGO_SCALE), + "default-font-family", + pango_font_description_get_family (vw), + "monospace-font-family", + pango_font_description_get_family (ms), + "default-monospace-font-size", pango_font_description_get_size (ms) / PANGO_SCALE, + "minimum-font-size", pango_font_description_get_size (min_size) / PANGO_SCALE, + NULL); + + manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (wk_editor)); + webkit_user_content_manager_remove_all_style_sheets (manager); + + style_sheet = webkit_user_style_sheet_new ( + stylesheet->str, + WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, + WEBKIT_USER_STYLE_LEVEL_USER, + NULL, + NULL); + + webkit_user_content_manager_add_style_sheet (manager, style_sheet); + + g_free (wk_editor->priv->current_user_stylesheet); + wk_editor->priv->current_user_stylesheet = g_string_free (stylesheet, FALSE); + + webkit_user_style_sheet_unref (style_sheet); + + pango_font_description_free (ms); + pango_font_description_free (vw); +} + +static gboolean +webkit_editor_get_html_mode (EWebKitEditor *wk_editor) +{ + return wk_editor->priv->html_mode; +} + +static gboolean +show_lose_formatting_dialog (EWebKitEditor *wk_editor) +{ + gboolean lose; + GtkWidget *toplevel; + GtkWindow *parent = NULL; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (wk_editor)); + + if (GTK_IS_WINDOW (toplevel)) + parent = GTK_WINDOW (toplevel); + + lose = e_util_prompt_user ( + parent, "org.gnome.evolution.mail", "prompt-on-composer-mode-switch", + "mail-composer:prompt-composer-mode-switch", NULL); + + if (!lose) { + /* Nothing has changed, but notify anyway */ + g_object_notify (G_OBJECT (wk_editor), "html-mode"); + return FALSE; + } + + return TRUE; +} + +static void +webkit_editor_set_html_mode (EWebKitEditor *wk_editor, + gboolean html_mode) +{ + gboolean convert = FALSE; + GVariant *result; + + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (html_mode == wk_editor->priv->html_mode) + return; + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "DOMCheckIfConversionNeeded", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(b)", &convert); + g_variant_unref (result); + } + + /* If toggling from HTML to the plain text mode, ask the user first if + * he wants to convert the content. */ + if (convert) { + if (!show_lose_formatting_dialog (wk_editor)) + return; + + webkit_editor_set_changed (wk_editor, TRUE); + } + + wk_editor->priv->html_mode = html_mode; + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "SetEditorHTMLMode", + g_variant_new ("(tbb)", current_page_id (wk_editor), html_mode, convert), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + /* Update fonts - in plain text we only want monospaced */ + webkit_editor_update_styles (E_CONTENT_EDITOR (wk_editor)); + + g_object_notify (G_OBJECT (wk_editor), "html-mode"); +} + +static void +set_convert_in_situ (EWebKitEditor *wk_editor, + gboolean value) +{ + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "SetConvertInSitu", + g_variant_new ("(tb)", current_page_id (wk_editor), value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + +} + +static void +webkit_editor_insert_content (EContentEditor *editor, + const gchar *content, + EContentEditorInsertContentFlags flags) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + /* It can happen that the view is not ready yet (it is in the middle of + * another load operation) so we have to queue the current operation and + * redo it again when the view is ready. This was happening when loading + * the stuff in EMailSignatureEditor. */ + if (wk_editor->priv->webkit_load_event != WEBKIT_LOAD_FINISHED || + wk_editor->priv->reload_in_progress) { + webkit_editor_queue_post_reload_operation ( + wk_editor, + (PostReloadOperationFunc) webkit_editor_insert_content, + g_strdup (content), + g_free, + flags); + return; + } + + if (!wk_editor->priv->web_extension) { + /* If the operation needs a web extension and it is not ready yet + * we need to schedule the current operation again a dispatch it + * when the extension is ready */ + if (!((flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) && + (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML) && + (strstr (content, "data-evo-draft") || + strstr (content, "data-evo-signature-plain-text-mode")))) { + webkit_editor_queue_post_reload_operation ( + wk_editor, + (PostReloadOperationFunc) webkit_editor_insert_content, + g_strdup (content), + g_free, + flags); + return; + } + } + + if ((flags & E_CONTENT_EDITOR_INSERT_CONVERT) && + !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) { + /* e_html_editor_view_convert_and_insert_plain_text + e_html_editor_view_convert_and_insert_html_to_plain_text + e_html_editor_view_insert_text */ + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMConvertAndInsertHTMLIntoSelection", + g_variant_new ( + "(tsb)", + current_page_id (wk_editor), + content, + (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } else if ((flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) && + (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML)) { + if ((strstr (content, "data-evo-draft") || + strstr (content, "data-evo-signature-plain-text-mode"))) { + wk_editor->priv->reload_in_progress = TRUE; + webkit_web_view_load_html (WEBKIT_WEB_VIEW (wk_editor), content, "file://"); + return; + } + + if (strstr (content, "data-evo-draft") && !(wk_editor->priv->html_mode)) { + if (content && *content) + set_convert_in_situ (wk_editor, TRUE); + wk_editor->priv->reload_in_progress = TRUE; + webkit_web_view_load_html (WEBKIT_WEB_VIEW (wk_editor), content, "file://"); + return; + } + + /* Only convert messages that are in HTML */ + if (!(wk_editor->priv->html_mode)) { + if (strstr (content, "<!-- text/html -->")) { + if (!show_lose_formatting_dialog (wk_editor)) { + wk_editor->priv->reload_in_progress = TRUE; + webkit_editor_set_html_mode (wk_editor, TRUE); + webkit_web_view_load_html ( + WEBKIT_WEB_VIEW (wk_editor), content, "file://"); + return; + } + } + if (content && *content) + set_convert_in_situ (wk_editor, TRUE); + } + + wk_editor->priv->reload_in_progress = TRUE; + webkit_web_view_load_html (WEBKIT_WEB_VIEW (wk_editor), content, "file://"); + } else if ((flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) && + (flags & E_CONTENT_EDITOR_INSERT_TEXT_PLAIN)) { + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMConvertContent", + g_variant_new ("(ts)", current_page_id (wk_editor), content), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } else if ((flags & E_CONTENT_EDITOR_INSERT_CONVERT) && + !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) && + !(flags & E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT)) { + /* e_html_editor_view_paste_as_text */ + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMConvertAndInsertHTMLIntoSelection", + g_variant_new ( + "(tsb)", current_page_id (wk_editor), content, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } else if ((flags & E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT) && + !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) { + /* e_html_editor_view_paste_clipboard_quoted */ + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMQuoteAndInsertTextIntoSelection", + g_variant_new ( + "(tsb)", current_page_id (wk_editor), content, (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML) != 0), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } else if (!(flags & E_CONTENT_EDITOR_INSERT_CONVERT) && + !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) { + /* e_html_editor_view_insert_html */ + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMInsertHTML", + g_variant_new ( + "(ts)", current_page_id (wk_editor), content), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } else + g_warning ("Unsupported flags combination (%d) in (%s)", flags, G_STRFUNC); +} + +static CamelMimePart * +create_part_for_inline_image_from_element_data (const gchar *element_src, + const gchar *name, + const gchar *id) +{ + CamelStream *stream; + CamelDataWrapper *wrapper; + CamelMimePart *part = NULL; + gsize decoded_size; + gssize size; + gchar *mime_type = NULL; + const gchar *base64_encoded_data; + guchar *base64_decoded_data = NULL; + + base64_encoded_data = strstr (element_src, ";base64,"); + if (!base64_encoded_data) + goto out; + + mime_type = g_strndup ( + element_src + 5, + base64_encoded_data - (strstr (element_src, "data:") + 5)); + + /* Move to actual data */ + base64_encoded_data += 8; + + base64_decoded_data = g_base64_decode (base64_encoded_data, &decoded_size); + + stream = camel_stream_mem_new (); + size = camel_stream_write ( + stream, (gchar *) base64_decoded_data, decoded_size, NULL, NULL); + + if (size == -1) + goto out; + + wrapper = camel_data_wrapper_new (); + camel_data_wrapper_construct_from_stream_sync ( + wrapper, stream, NULL, NULL); + g_object_unref (stream); + + camel_data_wrapper_set_mime_type (wrapper, mime_type); + + part = camel_mime_part_new (); + camel_medium_set_content (CAMEL_MEDIUM (part), wrapper); + g_object_unref (wrapper); + + camel_mime_part_set_content_id (part, id); + camel_mime_part_set_filename (part, name); + camel_mime_part_set_disposition (part, "inline"); + camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64); +out: + g_free (mime_type); + g_free (base64_decoded_data); + + return part; +} + +static GSList * +webkit_editor_get_parts_for_inline_images (GVariant *images) +{ + const gchar *element_src, *name, *id; + GVariantIter *iter; + GSList *parts = NULL; + + if (g_variant_check_format_string (images, "a(sss)", FALSE)) { + g_variant_get (images, "a(sss)", &iter); + while (g_variant_iter_loop (iter, "(&s&s&s)", &element_src, &name, &id)) { + CamelMimePart *part; + + part = create_part_for_inline_image_from_element_data ( + element_src, name, id); + parts = g_slist_prepend (parts, part); + } + g_variant_iter_free (iter); + } + + return parts ? g_slist_reverse (parts) : NULL; +} + +static gchar * +webkit_editor_get_content (EContentEditor *editor, + EContentEditorGetContentFlags flags, + const gchar *inline_images_from_domain, + GSList **inline_images_parts) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) + return g_strdup (""); + + if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) && + !(flags & E_CONTENT_EDITOR_GET_PROCESSED) && + !(flags & E_CONTENT_EDITOR_GET_BODY)) + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMEmbedStyleSheet", + g_variant_new ( + "(ts)", + current_page_id (wk_editor), + wk_editor->priv->current_user_stylesheet), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "DOMGetContent", + g_variant_new ( + "(tsi)", + current_page_id (wk_editor), + inline_images_from_domain ? inline_images_from_domain : "", + (gint32) flags), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) && + !(flags & E_CONTENT_EDITOR_GET_PROCESSED) && + !(flags & E_CONTENT_EDITOR_GET_BODY)) + webkit_editor_call_simple_extension_function ( + wk_editor, "DOMRemoveEmbeddedStyleSheet"); + + if (result) { + GVariant *images = NULL; + gchar *value = NULL; + + g_variant_get (result, "(sv)", &value, &images); + if (inline_images_parts) + *inline_images_parts = webkit_editor_get_parts_for_inline_images (images); + + if (images) + g_variant_unref (images); + + g_variant_unref (result); + + return value; + } + + return g_strdup (""); +} + +static gboolean +webkit_editor_can_undo (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return wk_editor->priv->can_undo; +} + +static void +webkit_editor_undo (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function (wk_editor, "DOMUndo"); +} + +static gboolean +webkit_editor_can_redo (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return wk_editor->priv->can_redo; +} + +static void +webkit_editor_redo (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + g_return_if_fail (E_IS_WEBKIT_EDITOR (editor)); + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function (wk_editor, "DOMRedo"); +} + +static void +webkit_editor_move_caret_on_coordinates (EContentEditor *editor, + gint x, + gint y, + gboolean cancel_if_not_collapsed) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "DOMMoveSelectionOnPoint", + g_variant_new ( + "(tiib)", current_page_id (wk_editor), x, y, cancel_if_not_collapsed), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); +} + +static void +webkit_editor_insert_emoticon (EContentEditor *editor, + EEmoticon *emoticon) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMInsertSmiley", + g_variant_new ( + "(ts)", current_page_id (wk_editor), e_emoticon_get_name (emoticon)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_insert_image_from_mime_part (EContentEditor *editor, + CamelMimePart *part) +{ + CamelDataWrapper *dw; + CamelStream *stream; + EWebKitEditor *wk_editor; + GByteArray *byte_array; + gchar *src, *base64_encoded, *mime_type, *cid_uri; + const gchar *cid, *name; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + stream = camel_stream_mem_new (); + dw = camel_medium_get_content (CAMEL_MEDIUM (part)); + g_return_if_fail (dw); + + mime_type = camel_data_wrapper_get_mime_type (dw); + camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL); + camel_stream_close (stream, NULL, NULL); + + byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream)); + + if (!byte_array->data) + return; + + base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len); + + name = camel_mime_part_get_filename (part); + /* Insert file name before new src */ + src = g_strconcat (name, ";data:", mime_type, ";base64,", base64_encoded, NULL); + + cid = camel_mime_part_get_content_id (part); + if (!cid) { + camel_mime_part_set_content_id (part, NULL); + cid = camel_mime_part_get_content_id (part); + } + cid_uri = g_strdup_printf ("cid:%s", cid); + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMAddNewInlineImageIntoList", + g_variant_new ("(tsss)", current_page_id (wk_editor), name, cid_uri, src), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + g_free (base64_encoded); + g_free (mime_type); + g_object_unref (stream); +} + +static void +webkit_editor_select_all (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_web_view_execute_editing_command ( + WEBKIT_WEB_VIEW (wk_editor), WEBKIT_EDITING_COMMAND_SELECT_ALL); +} + +static void +webkit_editor_selection_wrap (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function (wk_editor, "DOMSelectionWrap"); +} + +static gboolean +webkit_editor_selection_is_indented (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return wk_editor->priv->is_indented; +} + +static void +webkit_editor_selection_indent (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "DOMSelectionIndent"); +} + +static void +webkit_editor_selection_unindent (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "DOMSelectionUnindent"); +} + +static void +webkit_editor_cut (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + wk_editor->priv->copy_cut_actions_triggered = TRUE; + + webkit_editor_call_simple_extension_function_sync ( + wk_editor, "EEditorActionsSaveHistoryForCut"); + + webkit_web_view_execute_editing_command ( + WEBKIT_WEB_VIEW (wk_editor), WEBKIT_EDITING_COMMAND_CUT); +} + +static void +webkit_editor_copy (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + wk_editor->priv->copy_cut_actions_triggered = TRUE; + + webkit_web_view_execute_editing_command ( + WEBKIT_WEB_VIEW (wk_editor), WEBKIT_EDITING_COMMAND_COPY); +} + +static ESpellChecker * +webkit_editor_get_spell_checker (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL); + + return wk_editor->priv->spell_checker; +} + +static gchar * +webkit_editor_get_caret_word (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *ret_val = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return NULL; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "DOMGetCaretWord", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(s)", &ret_val); + g_variant_unref (result); + } + + return ret_val; +} + +static void +webkit_editor_set_spell_checking_languages (EContentEditor *editor, + const gchar **languages) +{ + EWebKitEditor *wk_editor; + WebKitWebContext *web_context; + + wk_editor = E_WEBKIT_EDITOR (editor); + web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (wk_editor)); + webkit_web_context_set_spell_checking_languages (web_context, (const gchar * const *) languages); +} + +static void +webkit_editor_set_spell_check_enabled (EWebKitEditor *wk_editor, + gboolean enable) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if ((wk_editor->priv->spell_check_enabled ? 1 : 0) == (enable ? 1 : 0)) + return; + + wk_editor->priv->spell_check_enabled = enable; + + webkit_editor_call_simple_extension_function ( + wk_editor, enable ? "DOMForceSpellCheck" : "DOMTurnSpellCheckOff"); + + g_object_notify (G_OBJECT (wk_editor), "spell-check-enabled"); +} + +static gboolean +webkit_editor_get_spell_check_enabled (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return wk_editor->priv->spell_check_enabled; +} + +static gboolean +webkit_editor_is_editable (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return webkit_web_view_is_editable (WEBKIT_WEB_VIEW (wk_editor)); +} + +static void +webkit_editor_set_editable (EWebKitEditor *wk_editor, + gboolean editable) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + return webkit_web_view_set_editable (WEBKIT_WEB_VIEW (wk_editor), editable); +} + +static gchar * +webkit_editor_get_current_signature_uid (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *ret_val= NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return NULL; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "DOMGetActiveSignatureUid", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(s)", &ret_val); + g_variant_unref (result); + } + + return ret_val; +} + +static gboolean +webkit_editor_is_ready (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + return !webkit_web_view_is_loading (WEBKIT_WEB_VIEW (wk_editor)) && wk_editor->priv->web_extension; +} + +static char * +webkit_editor_insert_signature (EContentEditor *editor, + const gchar *content, + gboolean is_html, + const gchar *signature_id, + gboolean *set_signature_from_message, + gboolean *check_if_signature_is_changed, + gboolean *ignore_next_signature_change) +{ + EWebKitEditor *wk_editor; + gchar *ret_val = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return NULL; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "DOMInsertSignature", + g_variant_new ( + "(tsbsbbb)", + current_page_id (wk_editor), + content ? content : "", + is_html, + signature_id, + *set_signature_from_message, + *check_if_signature_is_changed, + *ignore_next_signature_change), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get ( + result, + "(sbbb)", + &ret_val, + set_signature_from_message, + check_if_signature_is_changed, + ignore_next_signature_change); + g_variant_unref (result); + } + + return ret_val; +} + +static guint +webkit_editor_get_caret_position (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + guint ret_val = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "DOMGetCaretPosition", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + ret_val = g_variant_get_uint32 (result); + g_variant_unref (result); + } + + return ret_val; +} + +static guint +webkit_editor_get_caret_offset (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + guint ret_val = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "DOMGetCaretOffset", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + ret_val = g_variant_get_uint32 (result); + g_variant_unref (result); + } + + return ret_val; +} + +static void +webkit_editor_clear_undo_redo_history (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMClearUndoRedoHistory", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_replace_caret_word (EContentEditor *editor, + const gchar *replacement) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMReplaceCaretWord", + g_variant_new ("(ts)", current_page_id (wk_editor), replacement), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_finish_search (EWebKitEditor *wk_editor) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if (!wk_editor->priv->find_controller) + return; + + webkit_find_controller_search_finish (wk_editor->priv->find_controller); + + wk_editor->priv->performing_replace_all = FALSE; + wk_editor->priv->replaced_count = 0; + g_free (wk_editor->priv->replace_with); + wk_editor->priv->replace_with = NULL; + + if (wk_editor->priv->found_text_handler_id) { + g_signal_handler_disconnect (wk_editor->priv->find_controller, wk_editor->priv->found_text_handler_id); + wk_editor->priv->found_text_handler_id = 0; + } + + if (wk_editor->priv->failed_to_find_text_handler_id) { + g_signal_handler_disconnect (wk_editor->priv->find_controller, wk_editor->priv->failed_to_find_text_handler_id); + wk_editor->priv->failed_to_find_text_handler_id = 0; + } + + wk_editor->priv->find_controller = NULL; +} + +static guint32 /* WebKitFindOptions */ +find_flags_to_webkit_find_options (guint32 flags /* EContentEditorFindFlags */) +{ + guint32 options = 0; + + if (flags & E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE) + options |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE; + + if (flags & E_CONTENT_EDITOR_FIND_WRAP_AROUND) + options |= WEBKIT_FIND_OPTIONS_WRAP_AROUND; + + if (flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) + options |= WEBKIT_FIND_OPTIONS_BACKWARDS; + + return options; +} + +static void +webkit_find_controller_found_text_cb (WebKitFindController *find_controller, + guint match_count, + EWebKitEditor *wk_editor) +{ + if (wk_editor->priv->performing_replace_all) { + if (!wk_editor->priv->replaced_count) + wk_editor->priv->replaced_count = match_count; + + /* Repeatedly search for 'word', then replace selection by + * 'replacement'. Repeat until there's at least one occurrence of + * 'word' in the document */ + e_content_editor_insert_content ( + E_CONTENT_EDITOR (wk_editor), + wk_editor->priv->replace_with, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN); + + webkit_find_controller_search_next (find_controller); + } else { + e_content_editor_emit_find_done (E_CONTENT_EDITOR (wk_editor), match_count); + } +} + +static void +webkit_find_controller_failed_to_find_text_cb (WebKitFindController *find_controller, + EWebKitEditor *wk_editor) +{ + if (wk_editor->priv->performing_replace_all) { + guint replaced_count = wk_editor->priv->replaced_count; + + webkit_editor_finish_search (wk_editor); + e_content_editor_emit_replace_all_done (E_CONTENT_EDITOR (wk_editor), replaced_count); + } else { + e_content_editor_emit_find_done (E_CONTENT_EDITOR (wk_editor), 0); + } +} + +static void +webkit_editor_prepare_find_controller (EWebKitEditor *wk_editor) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + g_return_if_fail (wk_editor->priv->find_controller == NULL); + + wk_editor->priv->find_controller = webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (wk_editor)); + + wk_editor->priv->found_text_handler_id = g_signal_connect ( + wk_editor->priv->find_controller, "found-text", + G_CALLBACK (webkit_find_controller_found_text_cb), wk_editor); + + wk_editor->priv->failed_to_find_text_handler_id = g_signal_connect ( + wk_editor->priv->find_controller, "failed-to-find-text", + G_CALLBACK (webkit_find_controller_failed_to_find_text_cb), wk_editor); + + wk_editor->priv->performing_replace_all = FALSE; + wk_editor->priv->replaced_count = 0; + g_free (wk_editor->priv->replace_with); + wk_editor->priv->replace_with = NULL; +} + +static void +webkit_editor_find (EContentEditor *editor, + guint32 flags, + const gchar *text) +{ + EWebKitEditor *wk_editor; + guint32 wk_options; + gboolean needs_init; + + g_return_if_fail (E_IS_WEBKIT_EDITOR (editor)); + g_return_if_fail (text != NULL); + + wk_editor = E_WEBKIT_EDITOR (editor); + + wk_options = find_flags_to_webkit_find_options (flags); + + needs_init = !wk_editor->priv->find_controller; + if (needs_init) { + webkit_editor_prepare_find_controller (wk_editor); + } else { + needs_init = wk_options != webkit_find_controller_get_options (wk_editor->priv->find_controller) || + g_strcmp0 (text, webkit_find_controller_get_search_text (wk_editor->priv->find_controller)) != 0; + } + + if (needs_init) { + webkit_find_controller_search (wk_editor->priv->find_controller, text, wk_options, G_MAXUINT); + } else if ((flags & E_CONTENT_EDITOR_FIND_PREVIOUS) != 0) { + webkit_find_controller_search_previous (wk_editor->priv->find_controller); + } else { + webkit_find_controller_search_next (wk_editor->priv->find_controller); + } +} + +static void +webkit_editor_replace (EContentEditor *editor, + const gchar *replacement) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMSelectionReplace", + g_variant_new ("(ts)", current_page_id (wk_editor), replacement), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_replace_all (EContentEditor *editor, + guint32 flags, + const gchar *find_text, + const gchar *replace_with) +{ + EWebKitEditor *wk_editor; + guint32 wk_options; + + g_return_if_fail (E_IS_WEBKIT_EDITOR (editor)); + g_return_if_fail (find_text != NULL); + g_return_if_fail (replace_with != NULL); + + wk_editor = E_WEBKIT_EDITOR (editor); + wk_options = find_flags_to_webkit_find_options (flags); + + if (!wk_editor->priv->find_controller) + webkit_editor_prepare_find_controller (wk_editor); + + g_free (wk_editor->priv->replace_with); + wk_editor->priv->replace_with = g_strdup (replace_with); + + wk_editor->priv->performing_replace_all = TRUE; + wk_editor->priv->replaced_count = 0; + + webkit_find_controller_search (wk_editor->priv->find_controller, find_text, wk_options, G_MAXUINT); +} + +static void +webkit_editor_selection_save (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "DOMSaveSelection"); +} + +static void +webkit_editor_selection_restore (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "DOMRestoreSelection"); +} + +static void +webkit_editor_delete_cell_contents (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorDialogDeleteCellContents"); +} + +static void +webkit_editor_delete_column (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorDialogDeleteColumn"); +} + +static void +webkit_editor_delete_row (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorDialogDeleteRow"); +} + +static void +webkit_editor_delete_table (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorDialogDeleteTable"); +} + +static void +webkit_editor_insert_column_after (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorDialogInsertColumnAfter"); +} + +static void +webkit_editor_insert_column_before (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorDialogInsertColumnBefore"); +} + + +static void +webkit_editor_insert_row_above (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorDialogInsertRowAbove"); +} + +static void +webkit_editor_insert_row_below (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorDialogInsertRowBelow"); +} + +static gboolean +webkit_editor_on_h_rule_dialog_open (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gboolean value = FALSE; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return FALSE; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "EEditorHRuleDialogFindHRule", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(b)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_on_h_rule_dialog_close (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorHRuleDialogOnClose"); +} + +static void +webkit_editor_h_rule_set_align (EContentEditor *editor, + const gchar *value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-hr", "align", value); +} + +static gchar * +webkit_editor_h_rule_get_align (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *value = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-hr", "align"); + if (result) { + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_h_rule_set_size (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + gchar *size; + + wk_editor = E_WEBKIT_EDITOR (editor); + + size = g_strdup_printf ("%d", value); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-hr", "size", size); + + g_free (size); +} + +static gint +webkit_editor_h_rule_get_size (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gint size = 0; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-hr", "size"); + if (result) { + const gchar *value; + + g_variant_get (result, "(&s)", &value); + if (value && *value) + size = atoi (value); + + if (size == 0) + size = 2; + + g_variant_unref (result); + } + + return size; +} + +static void +webkit_editor_h_rule_set_width (EContentEditor *editor, + gint value, + EContentEditorUnit unit) +{ + EWebKitEditor *wk_editor; + gchar *width; + + wk_editor = E_WEBKIT_EDITOR (editor); + + width = g_strdup_printf ( + "%d%s", + value, + (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "px" : "%"); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-hr", "width", width); + + g_free (width); +} + +static gint +webkit_editor_h_rule_get_width (EContentEditor *editor, + EContentEditorUnit *unit) +{ + EWebKitEditor *wk_editor; + gint value = 0; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + *unit = E_CONTENT_EDITOR_UNIT_PIXEL; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-hr", "width"); + if (result) { + const gchar *width; + g_variant_get (result, "(&s)", &width); + if (width && *width) { + value = atoi (width); + if (strstr (width, "%")) + *unit = E_CONTENT_EDITOR_UNIT_PERCENTAGE; + } + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_h_rule_set_no_shade (EContentEditor *editor, + gboolean value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (value) + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-hr", "noshade", ""); + else + webkit_editor_remove_element_attribute ( + wk_editor, "#-x-evo-current-hr", "noshade"); +} + +static gboolean +webkit_editor_h_rule_get_no_shade (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gboolean no_shade = FALSE; + + wk_editor = E_WEBKIT_EDITOR (editor); + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return FALSE; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ElementHasAttribute", + g_variant_new ("(tss)", current_page_id (wk_editor), "-x-evo-current-hr", "noshade"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(b)", &no_shade); + g_variant_unref (result); + } + + return no_shade; +} + +static void +webkit_editor_on_image_dialog_open (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorImageDialogMarkImage"); +} + +static void +webkit_editor_on_image_dialog_close (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorImageDialogSaveHistoryOnExit"); +} + +static void +webkit_editor_insert_image (EContentEditor *editor, + const gchar *image_uri) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMSelectionInsertImage", + g_variant_new ("(ts)", current_page_id (wk_editor), image_uri), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_replace_image_src (EWebKitEditor *wk_editor, + const gchar *selector, + const gchar *image_uri) +{ + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "DOMReplaceImageSrc", + g_variant_new ("(tss)", current_page_id (wk_editor), selector, image_uri), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_image_set_src (EContentEditor *editor, + const gchar *value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_replace_image_src ( + wk_editor, "img#-x-evo-current-img", value); +} + +static gchar * +webkit_editor_image_get_src (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *value = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-img", "data-uri"); + + if (result) { + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_image_set_alt (EContentEditor *editor, + const gchar *value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-img", "alt", value); +} + +static gchar * +webkit_editor_image_get_alt (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *value = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-img", "alt"); + + if (result) { + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_image_set_url (EContentEditor *editor, + const gchar *value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorImageDialogSetElementUrl", + g_variant_new ("(ts)", current_page_id (wk_editor), value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gchar * +webkit_editor_image_get_url (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gchar *value = NULL; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return NULL; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "EEditorImageDialogGetElementUrl", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_image_set_vspace (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "ImageElementSetVSpace", + g_variant_new ( + "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gint +webkit_editor_image_get_vspace (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ImageElementGetVSpace", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(i)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_image_set_hspace (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "ImageElementSetHSpace", + g_variant_new ( + "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gint +webkit_editor_image_get_hspace (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ImageElementGetHSpace", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(i)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_image_set_border (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + gchar *border; + + wk_editor = E_WEBKIT_EDITOR (editor); + + border = g_strdup_printf ("%d", value); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-img", "border", border); + + g_free (border); +} + +static gint +webkit_editor_image_get_border (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gint value = 0; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-img", "border"); + + if (result) { + const gchar *border; + g_variant_get (result, "(&s)", &border); + if (border && *border) + value = atoi (border); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_image_set_align (EContentEditor *editor, + const gchar *value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-img", "align", value); +} + +static gchar * +webkit_editor_image_get_align (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *value = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-img", "align"); + + if (result) { + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return value; +} + +static gint32 +webkit_editor_image_get_natural_width (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint32 value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ImageElementGetNaturalWidth", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(i)", &value); + g_variant_unref (result); + } + + return value; +} + +static gint32 +webkit_editor_image_get_natural_height (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint32 value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ImageElementGetNaturalHeight", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(i)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_image_set_height (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "ImageElementSetHeight", + g_variant_new ( + "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_image_set_width (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "ImageElementSetWidth", + g_variant_new ( + "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_image_set_height_follow (EContentEditor *editor, + gboolean value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (value) + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-img", "style", "height: auto;"); + else + webkit_editor_remove_element_attribute ( + wk_editor, "#-x-evo-current-img", "style"); +} + +static void +webkit_editor_image_set_width_follow (EContentEditor *editor, + gboolean value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (value) + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-img", "style", "width: auto;"); + else + webkit_editor_remove_element_attribute ( + wk_editor, "#-x-evo-current-img", "style"); +} + +static gint32 +webkit_editor_image_get_width (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint32 value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ImageElementGetWidth", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(i)", &value); + g_variant_unref (result); + } + + return value; +} + +static gint32 +webkit_editor_image_get_height (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint32 value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ImageElementGetHeight", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(i)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_selection_unlink (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorLinkDialogUnlink"); +} + +static void +webkit_editor_on_link_dialog_open (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorLinkDialogOnOpen"); +} + +static void +webkit_editor_on_link_dialog_close (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorLinkDialogOnClose"); +} + +static void +webkit_editor_link_set_values (EContentEditor *editor, + const gchar *href, + const gchar *text) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorLinkDialogOk", + g_variant_new ("(tss)", current_page_id (wk_editor), href, text), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_link_get_values (EContentEditor *editor, + gchar **href, + gchar **text) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "EEditorLinkDialogShow", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(ss)", href, text); + g_variant_unref (result); + } else { + *href = NULL; + *text = NULL; + } +} + +static void +webkit_editor_set_alignment (EWebKitEditor *wk_editor, + EContentEditorAlignment value) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + webkit_editor_set_format_int ( + wk_editor, "DOMSelectionSetAlignment", (gint32) value); +} + +static EContentEditorAlignment +webkit_editor_get_alignment (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), E_CONTENT_EDITOR_ALIGNMENT_LEFT); + + return wk_editor->priv->alignment; +} + +static void +webkit_editor_set_block_format (EWebKitEditor *wk_editor, + EContentEditorBlockFormat value) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + webkit_editor_set_format_int ( + wk_editor, "DOMSelectionSetBlockFormat", (gint32) value); +} + +static EContentEditorBlockFormat +webkit_editor_get_block_format (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), E_CONTENT_EDITOR_BLOCK_FORMAT_NONE); + + return wk_editor->priv->block_format; +} + +static void +webkit_editor_set_background_color (EWebKitEditor *wk_editor, + const GdkRGBA *value) +{ + gchar *color; + + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if (gdk_rgba_equal (value, wk_editor->priv->background_color)) + return; + + color = g_strdup_printf ("#%06x", e_rgba_to_value (value)); + + if (wk_editor->priv->background_color) + gdk_rgba_free (wk_editor->priv->background_color); + + wk_editor->priv->background_color = gdk_rgba_copy (value); + + webkit_editor_set_format_string ( + wk_editor, + "background-color", + "DOMSelectionSetBackgroundColor", + color); + + g_free (color); +} + +static const GdkRGBA * +webkit_editor_get_background_color (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return NULL; + } + + if (!wk_editor->priv->html_mode || !wk_editor->priv->background_color) + return &white; + + return wk_editor->priv->background_color; +} + +static void +webkit_editor_set_font_name (EWebKitEditor *wk_editor, + const gchar *value) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + wk_editor->priv->font_name = g_strdup (value); + + webkit_editor_set_format_string ( + wk_editor, "font-name", "DOMSelectionSetFontName", value); +} + +static const gchar * +webkit_editor_get_font_name (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL); + + return wk_editor->priv->font_name; +} + +static void +webkit_editor_set_font_color (EWebKitEditor *wk_editor, + const GdkRGBA *value) +{ + gchar *color; + + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if (gdk_rgba_equal (value, wk_editor->priv->font_color)) + return; + + color = g_strdup_printf ("#%06x", e_rgba_to_value (value)); + + if (wk_editor->priv->font_color) + gdk_rgba_free (wk_editor->priv->font_color); + + wk_editor->priv->font_color = gdk_rgba_copy (value); + + webkit_editor_set_format_string ( + wk_editor, + "font-color", + "DOMSelectionSetFontColor", + color); + + g_free (color); +} + +static const GdkRGBA * +webkit_editor_get_font_color (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return NULL; + } + + if (!wk_editor->priv->html_mode || !wk_editor->priv->font_color) + return &black; + + return wk_editor->priv->font_color; +} + +static void +webkit_editor_set_font_size (EWebKitEditor *wk_editor, + gint value) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if (wk_editor->priv->font_size == value) + return; + + wk_editor->priv->font_size = value; + + webkit_editor_set_format_int ( + wk_editor, "DOMSelectionSetFontSize", value); +} + +static gint +webkit_editor_get_font_size (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), -1); + + return wk_editor->priv->font_size; +} + +static void +webkit_editor_set_style_flag (EWebKitEditor *wk_editor, + EContentEditorStyleFlags flag, + gboolean do_set, + const gchar *dom_function_name) +{ + g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor)); + + if (((wk_editor->priv->style_flags & flag) != 0 ? 1 : 0) == (do_set ? 1 : 0)) + return; + + wk_editor->priv->style_flags = (wk_editor->priv->style_flags & ~flag) | (do_set ? flag : 0); + + webkit_editor_set_format_boolean (wk_editor, dom_function_name, do_set); +} + +static gboolean +webkit_editor_get_style_flag (EWebKitEditor *wk_editor, + EContentEditorStyleFlags flag) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE); + + return (wk_editor->priv->style_flags & flag) != 0; +} + +static void +webkit_editor_page_set_text_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EWebKitEditor *wk_editor; + gchar *color; + + wk_editor = E_WEBKIT_EDITOR (editor); + + color = g_strdup_printf ("#%06x", e_rgba_to_value (value)); + + webkit_editor_set_element_attribute (wk_editor, "body", "text", color); + + g_free (color); +} + +static void +webkit_editor_page_get_text_color (EContentEditor *editor, + GdkRGBA *color) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + goto theme; + + result = webkit_editor_get_element_attribute (wk_editor, "body", "text"); + if (result) { + const gchar *value; + + g_variant_get (result, "(&s)", &value); + if (!value || !*value || !gdk_rgba_parse (color, value)) { + g_variant_unref (result); + goto theme; + } + g_variant_unref (result); + return; + } + + theme: + e_utils_get_theme_color ( + GTK_WIDGET (wk_editor), + "theme_text_color", + E_UTILS_DEFAULT_THEME_TEXT_COLOR, + color); +} + +static void +webkit_editor_page_set_background_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EWebKitEditor *wk_editor; + gchar *color; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (value->alpha != 0.0) + color = g_strdup_printf ("#%06x", e_rgba_to_value (value)); + else + color = g_strdup (""); + + webkit_editor_set_element_attribute (wk_editor, "body", "bgcolor", color); + + g_free (color); +} + +static void +webkit_editor_page_get_background_color (EContentEditor *editor, + GdkRGBA *color) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + goto theme; + + result = webkit_editor_get_element_attribute (wk_editor, "body", "bgcolor"); + if (result) { + const gchar *value; + + g_variant_get (result, "(&s)", &value); + if (!value || !*value || !gdk_rgba_parse (color, value)) { + g_variant_unref (result); + goto theme; + } + g_variant_unref (result); + return; + } + + theme: + e_utils_get_theme_color ( + GTK_WIDGET (wk_editor), + "theme_base_color", + E_UTILS_DEFAULT_THEME_BASE_COLOR, + color); +} + +static void +webkit_editor_page_set_link_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EWebKitEditor *wk_editor; + gchar *color; + + wk_editor = E_WEBKIT_EDITOR (editor); + + color = g_strdup_printf ("#%06x", e_rgba_to_value (value)); + + webkit_editor_set_element_attribute (wk_editor, "body", "link", color); + + g_free (color); +} + +static void +webkit_editor_page_get_link_color (EContentEditor *editor, + GdkRGBA *color) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + goto theme; + + result = webkit_editor_get_element_attribute (wk_editor, "body", "link"); + if (result) { + const gchar *value; + + g_variant_get (result, "(&s)", &value); + if (!value || !*value || !gdk_rgba_parse (color, value)) { + g_variant_unref (result); + goto theme; + } + g_variant_unref (result); + return; + } + + theme: + color->alpha = 1; + color->red = 0; + color->green = 0; + color->blue = 1; +} + +static void +webkit_editor_page_set_visited_link_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EWebKitEditor *wk_editor; + gchar *color; + + wk_editor = E_WEBKIT_EDITOR (editor); + + color = g_strdup_printf ("#%06x", e_rgba_to_value (value)); + + webkit_editor_set_element_attribute (wk_editor, "body", "vlink", color); + + g_free (color); +} + +static void +webkit_editor_page_get_visited_link_color (EContentEditor *editor, + GdkRGBA *color) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + goto theme; + + result = webkit_editor_get_element_attribute (wk_editor, "body", "vlink"); + if (result) { + const gchar *value; + + g_variant_get (result, "(&s)", &value); + if (!value || !*value || !gdk_rgba_parse (color, value)) { + g_variant_unref (result); + goto theme; + } + g_variant_unref (result); + return; + } + + theme: + color->alpha = 1; + color->red = 1; + color->green = 0; + color->blue = 0; +} + +static void +webkit_editor_on_page_dialog_open (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorPageDialogSaveHistory"); +} + +static void +webkit_editor_on_page_dialog_close (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorPageDialogSaveHistoryOnExit"); +} + +static gchar * +webkit_editor_page_get_background_image_uri (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return NULL; + + result = webkit_editor_get_element_attribute (wk_editor, "body", "data-uri"); + if (result) { + gchar *value; + + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return NULL; +} + +static void +webkit_editor_page_set_background_image_uri (EContentEditor *editor, + const gchar *uri) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (uri && *uri) + webkit_editor_replace_image_src (wk_editor, "body", uri); + else { + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "RemoveImageAttributesFromElementBySelector", + g_variant_new ("(ts)", current_page_id (wk_editor), "body"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } +} + +static void +webkit_editor_on_cell_dialog_open (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogMarkCurrentCellElement", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_on_cell_dialog_close (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorCellDialogSaveHistoryOnExit"); +} + +static void +webkit_editor_cell_set_v_align (EContentEditor *editor, + const gchar *value, + EContentEditorScope scope) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogSetElementVAlign", + g_variant_new ("(tsi)", current_page_id (wk_editor), value, (gint32) scope), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gchar * +webkit_editor_cell_get_v_align (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *value = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return NULL; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-cell", "valign"); + if (result) { + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_cell_set_align (EContentEditor *editor, + const gchar *value, + EContentEditorScope scope) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogSetElementAlign", + g_variant_new ("(tsi)", current_page_id (wk_editor), value, (gint32) scope), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gchar * +webkit_editor_cell_get_align (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *value = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return NULL; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-cell", "align"); + if (result) { + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_cell_set_wrap (EContentEditor *editor, + gboolean value, + EContentEditorScope scope) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogSetElementNoWrap", + g_variant_new ("(tbi)", current_page_id (wk_editor), !value, (gint32) scope), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gboolean +webkit_editor_cell_get_wrap (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gboolean value = FALSE; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return FALSE; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return FALSE; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "TableCellElementGetNoWrap", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(b)", &value); + value = !value; + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_cell_set_header_style (EContentEditor *editor, + gboolean value, + EContentEditorScope scope) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (!wk_editor->priv->html_mode) + return; + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogSetElementHeaderStyle", + g_variant_new ("(tbi)", current_page_id (wk_editor), value, (gint32) scope), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static gboolean +webkit_editor_cell_is_header (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gboolean value = FALSE; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return FALSE; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return FALSE; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "ElementGetTagName", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + const gchar *tag_name; + + g_variant_get (result, "(&s)", &tag_name); + value = g_ascii_strncasecmp (tag_name, "TH", 2) == 0; + g_variant_unref (result); + } + + return value; +} + +static gint +webkit_editor_cell_get_width (EContentEditor *editor, + EContentEditorUnit *unit) +{ + EWebKitEditor *wk_editor; + gint value = 0; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + *unit = E_CONTENT_EDITOR_UNIT_AUTO; + + if (!wk_editor->priv->html_mode) + return 0; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-cell", "width"); + + if (result) { + const gchar *width; + + g_variant_get (result, "(&s)", &width); + if (width && *width) { + value = atoi (width); + if (strstr (width, "%")) + *unit = E_CONTENT_EDITOR_UNIT_PERCENTAGE; + else if (g_ascii_strncasecmp (width, "auto", 4) != 0) + *unit = E_CONTENT_EDITOR_UNIT_PIXEL; + } + g_variant_unref (result); + } + + return value; +} + +static gint +webkit_editor_cell_get_row_span (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gint value = 0; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return 0; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "TableCellElementGetRowSpan", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(i)", &value); + g_variant_unref (result); + } + + return value; +} + +static gint +webkit_editor_cell_get_col_span (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gint value = 0; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return 0; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "TableCellElementGetColSpan", + g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(i)", &value); + g_variant_unref (result); + } + + return value; +} + +static gchar * +webkit_editor_cell_get_background_image_uri (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return NULL; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-cell", "data-uri"); + if (result) { + gchar *value; + + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return NULL; +} + +static void +webkit_editor_cell_get_background_color (EContentEditor *editor, + GdkRGBA *color) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + goto exit; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-cell", "bgcolor"); + if (result) { + const gchar *value; + + g_variant_get (result, "(&s)", &value); + if (!value || !*value || !gdk_rgba_parse (color, value)) { + g_variant_unref (result); + goto exit; + } + g_variant_unref (result); + return; + } + + exit: + *color = transparent; +} + +static void +webkit_editor_cell_set_row_span (EContentEditor *editor, + gint value, + EContentEditorScope scope) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogSetElementRowSpan", + g_variant_new ("(tii)", current_page_id (wk_editor), value, (gint32) scope), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_cell_set_col_span (EContentEditor *editor, + gint value, + EContentEditorScope scope) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogSetElementColSpan", + g_variant_new ("(tii)", current_page_id (wk_editor), value, (gint32) scope), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +webkit_editor_cell_set_width (EContentEditor *editor, + gint value, + EContentEditorUnit unit, + EContentEditorScope scope) +{ + EWebKitEditor *wk_editor; + gchar *width; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (unit == E_CONTENT_EDITOR_UNIT_AUTO) + width = g_strdup ("auto"); + else + width = g_strdup_printf ( + "%d%s", + value, + (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "px" : "%"); + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogSetElementWidth", + g_variant_new ("(tsi)", current_page_id (wk_editor), width, (gint32) scope), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + g_free (width); +} + +static void +webkit_editor_cell_set_background_color (EContentEditor *editor, + const GdkRGBA *value, + EContentEditorScope scope) +{ + EWebKitEditor *wk_editor; + gchar *color; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (value->alpha != 0.0) + color = g_strdup_printf ("#%06x", e_rgba_to_value (value)); + else + color = g_strdup (""); + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorCellDialogSetElementBgColor", + g_variant_new ("(tsi)", current_page_id (wk_editor), color, (gint32) scope), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + g_free (color); +} + +static void +webkit_editor_cell_set_background_image_uri (EContentEditor *editor, + const gchar *uri) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (!wk_editor->priv->html_mode) + return; + + if (uri && *uri) + webkit_editor_replace_image_src (wk_editor, "#-x-evo-current-cell", uri); + else { + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "RemoveImageAttributesFromElementBySelector", + g_variant_new ("(ts)", current_page_id (wk_editor), "#-x-evo-current-cell"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } +} + +static void +webkit_editor_table_set_row_count (EContentEditor *editor, + guint value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorTableDialogSetRowCount", + g_variant_new ("(tu)", current_page_id (wk_editor), value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static guint +webkit_editor_table_get_row_count (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + guint value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return 0; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "EEditorTableDialogGetRowCount", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(u)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_table_set_column_count (EContentEditor *editor, + guint value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "EEditorTableDialogSetColumnCount", + g_variant_new ("(tu)", current_page_id (wk_editor), value), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static guint +webkit_editor_table_get_column_count (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + guint value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return 0; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return 0; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "EEditorTableDialogGetColumnCount", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(u)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_table_set_width (EContentEditor *editor, + gint value, + EContentEditorUnit unit) +{ + EWebKitEditor *wk_editor; + gchar *width; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (unit == E_CONTENT_EDITOR_UNIT_AUTO) + width = g_strdup ("auto"); + else + width = g_strdup_printf ( + "%d%s", + value, + (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "px" : "%"); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-table", "width", width); + + g_free (width); +} + +static guint +webkit_editor_table_get_width (EContentEditor *editor, + EContentEditorUnit *unit) +{ + EWebKitEditor *wk_editor; + guint value = 0; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + *unit = E_CONTENT_EDITOR_UNIT_PIXEL; + + if (!wk_editor->priv->html_mode) + return 0; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-table", "width"); + + if (result) { + const gchar *width; + + g_variant_get (result, "(&s)", &width); + if (width && *width) { + value = atoi (width); + if (strstr (width, "%")) + *unit = E_CONTENT_EDITOR_UNIT_PERCENTAGE; + else if (g_ascii_strncasecmp (width, "auto", 4) != 0) + *unit = E_CONTENT_EDITOR_UNIT_PIXEL; + } + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_table_set_align (EContentEditor *editor, + const gchar *value) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return; + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-table", "align", value); +} + +static gchar * +webkit_editor_table_get_align (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + gchar *value = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return NULL; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-table", "align"); + if (result) { + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_table_set_padding (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + gchar *padding; + + wk_editor = E_WEBKIT_EDITOR (editor); + + padding = g_strdup_printf ("%d", value); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-table", "cellpadding", padding); + + g_free (padding); +} + +static gint +webkit_editor_table_get_padding (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-table", "cellpadding"); + + if (result) { + const gchar *padding; + + g_variant_get (result, "(&s)", &padding); + if (padding && *padding) + value = atoi (padding); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_table_set_spacing (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + gchar *spacing; + + wk_editor = E_WEBKIT_EDITOR (editor); + + spacing = g_strdup_printf ("%d", value); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-table", "cellspacing", spacing); + + g_free (spacing); +} + +static gint +webkit_editor_table_get_spacing (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-table", "cellspacing"); + + if (result) { + const gchar *spacing; + + g_variant_get (result, "(&s)", &spacing); + if (spacing && *spacing) + value = atoi (spacing); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_table_set_border (EContentEditor *editor, + gint value) +{ + EWebKitEditor *wk_editor; + gchar *border; + + wk_editor = E_WEBKIT_EDITOR (editor); + + border = g_strdup_printf ("%d", value); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-table", "border", border); + + g_free (border); +} + +static gint +webkit_editor_table_get_border (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gint value = 0; + + wk_editor = E_WEBKIT_EDITOR (editor); + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-table", "border"); + + if (result) { + const gchar *border; + + g_variant_get (result, "(&s)", &border); + if (border && *border) + value = atoi (border); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_table_get_background_color (EContentEditor *editor, + GdkRGBA *color) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + goto exit; + + result = webkit_editor_get_element_attribute ( + wk_editor, "#-x-evo-current-table", "bgcolor"); + if (result) { + const gchar *value; + + g_variant_get (result, "(&s)", &value); + if (!value || !*value || !gdk_rgba_parse (color, value)) { + g_variant_unref (result); + goto exit; + } + g_variant_unref (result); + return; + } + + exit: + *color = transparent; +} + +static void +webkit_editor_table_set_background_color (EContentEditor *editor, + const GdkRGBA *value) +{ + EWebKitEditor *wk_editor; + gchar *color; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (value->alpha != 0.0) + color = g_strdup_printf ("#%06x", e_rgba_to_value (value)); + else + color = g_strdup (""); + + webkit_editor_set_element_attribute ( + wk_editor, "#-x-evo-current-table", "bgcolor", color); + + g_free (color); +} + +static gchar * +webkit_editor_table_get_background_image_uri (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->html_mode) + return NULL; + + result = webkit_editor_get_element_attribute (wk_editor, "#-x-evo-current-table", "data-uri"); + if (result) { + gchar *value; + + g_variant_get (result, "(s)", &value); + g_variant_unref (result); + } + + return NULL; +} + +static void +webkit_editor_table_set_background_image_uri (EContentEditor *editor, + const gchar *uri) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return; + } + + if (!wk_editor->priv->html_mode) + return; + + if (uri && *uri) + webkit_editor_replace_image_src (wk_editor, "#-x-evo-current-table", uri); + else { + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "RemoveImageAttributesFromElementBySelector", + g_variant_new ("(ts)", current_page_id (wk_editor), "#-x-evo-current-table"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } +} + +static gboolean +webkit_editor_on_table_dialog_open (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + GVariant *result; + gboolean value = FALSE; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return FALSE; + } + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + "EEditorTableDialogShow", + g_variant_new ("(t)", current_page_id (wk_editor)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + if (result) { + g_variant_get (result, "(b)", &value); + g_variant_unref (result); + } + + return value; +} + +static void +webkit_editor_on_table_dialog_close (EContentEditor *editor) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + webkit_editor_call_simple_extension_function ( + wk_editor, "EEditorTableDialogSaveHistoryOnExit"); + + webkit_editor_finish_search (E_WEBKIT_EDITOR (editor)); +} + +static void +webkit_editor_on_spell_check_dialog_open (EContentEditor *editor) +{ +} + +static void +webkit_editor_on_spell_check_dialog_close (EContentEditor *editor) +{ + webkit_editor_finish_search (E_WEBKIT_EDITOR (editor)); +} + +static gchar * +move_to_another_word (EContentEditor *editor, + const gchar *word, + const gchar *dom_function) +{ + EWebKitEditor *wk_editor; + gchar **active_languages; + gchar *another_word = NULL; + GVariant *result; + + wk_editor = E_WEBKIT_EDITOR (editor); + + if (!wk_editor->priv->web_extension) { + g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC); + return NULL; + } + + active_languages = e_spell_checker_list_active_languages ( + wk_editor->priv->spell_checker, NULL); + if (!active_languages) + return NULL; + + result = g_dbus_proxy_call_sync_wrapper ( + wk_editor->priv->web_extension, + dom_function, + g_variant_new ( + "(ts^as)", current_page_id (wk_editor), word ? word : "", active_languages), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + + g_strfreev (active_languages); + + if (result) { + g_variant_get (result, "(s)", &another_word); + g_variant_unref (result); + } + + return another_word; +} + +static gchar * +webkit_editor_spell_check_next_word (EContentEditor *editor, + const gchar *word) +{ + return move_to_another_word (editor, word, "EEditorSpellCheckDialogNext"); +} + +static gchar * +webkit_editor_spell_check_prev_word (EContentEditor *editor, + const gchar *word) +{ + return move_to_another_word (editor, word, "EEditorSpellCheckDialogPrev"); +} + +static void +webkit_editor_on_replace_dialog_open (EContentEditor *editor) +{ +} + +static void +webkit_editor_on_replace_dialog_close (EContentEditor *editor) +{ + webkit_editor_finish_search (E_WEBKIT_EDITOR (editor)); +} + +static void +webkit_editor_on_find_dialog_open (EContentEditor *editor) +{ +} + +static void +webkit_editor_on_find_dialog_close (EContentEditor *editor) +{ + webkit_editor_finish_search (E_WEBKIT_EDITOR (editor)); +} + +static GDBusProxy * +webkit_editor_get_web_extension (EWebKitEditor *wk_editor) +{ + g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL); + + return wk_editor->priv->web_extension; +} + +static void +webkit_editor_constructed (GObject *object) +{ + EWebKitEditor *wk_editor; + gchar **languages; + WebKitWebContext *web_context; + WebKitSettings *web_settings; + WebKitWebView *web_view; + + G_OBJECT_CLASS (e_webkit_editor_parent_class)->constructed (object); + + wk_editor = E_WEBKIT_EDITOR (object); + web_view = WEBKIT_WEB_VIEW (wk_editor); + + /* Give spell check languages to WebKit */ + languages = e_spell_checker_list_active_languages (wk_editor->priv->spell_checker, NULL); + + web_context = webkit_web_view_get_context (web_view); + webkit_web_context_set_spell_checking_enabled (web_context, TRUE); + webkit_web_context_set_spell_checking_languages (web_context, (const gchar * const *) languages); + g_strfreev (languages); + + webkit_web_view_set_editable (web_view, TRUE); + + web_settings = webkit_web_view_get_settings (web_view); + webkit_settings_set_allow_file_access_from_file_urls (web_settings, TRUE); + webkit_settings_set_enable_developer_extras (web_settings, TRUE); + + /* Make WebKit think we are displaying a local file, so that it + * does not block loading resources from file:// protocol */ + webkit_web_view_load_html (WEBKIT_WEB_VIEW (object), "", "file://"); +} + +static GObjectConstructParam* +find_property (guint n_properties, + GObjectConstructParam* properties, + GParamSpec* param_spec) +{ + while (n_properties--) { + if (properties->pspec == param_spec) + return properties; + properties++; + } + + return NULL; +} + +static GObject * +webkit_editor_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObjectClass* object_class; + GParamSpec* param_spec; + GObjectConstructParam *param = NULL; + + object_class = G_OBJECT_CLASS (g_type_class_ref (type)); + g_return_val_if_fail (object_class != NULL, NULL); + + if (construct_properties && n_construct_properties != 0) { + param_spec = g_object_class_find_property (object_class, "settings"); + if ((param = find_property (n_construct_properties, construct_properties, param_spec))) + g_value_take_object (param->value, e_web_view_get_default_webkit_settings ()); + param_spec = g_object_class_find_property (object_class, "user-content-manager"); + if ((param = find_property (n_construct_properties, construct_properties, param_spec))) + g_value_take_object (param->value, webkit_user_content_manager_new ()); + param_spec = g_object_class_find_property (object_class, "web-context"); + if ((param = find_property (n_construct_properties, construct_properties, param_spec))) { + /* Share one web_context between all editors, thus there is one WebProcess + for all the editors (and one for the preview). */ + static gpointer web_context = NULL; + + if (!web_context) { + web_context = webkit_web_context_new (); + + webkit_web_context_set_cache_model (web_context, WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); + webkit_web_context_set_web_extensions_directory (web_context, EVOLUTION_WEB_EXTENSIONS_WEBKIT_EDITOR_DIR); + + g_object_add_weak_pointer (G_OBJECT (web_context), &web_context); + } else { + g_object_ref (web_context); + } + + g_value_take_object (param->value, web_context); + } + } + + g_type_class_unref (object_class); + + return G_OBJECT_CLASS (e_webkit_editor_parent_class)->constructor (type, n_construct_properties, construct_properties); +} + +static void +webkit_editor_dispose (GObject *object) +{ + EWebKitEditorPrivate *priv; + + priv = E_WEBKIT_EDITOR_GET_PRIVATE (object); + + if (priv->aliasing_settings != NULL) { + g_signal_handlers_disconnect_by_data (priv->aliasing_settings, object); + g_object_unref (priv->aliasing_settings); + priv->aliasing_settings = NULL; + } + + if (priv->current_user_stylesheet != NULL) { + g_free (priv->current_user_stylesheet); + priv->current_user_stylesheet = NULL; + } + + if (priv->font_settings != NULL) { + g_signal_handlers_disconnect_by_data (priv->font_settings, object); + g_object_unref (priv->font_settings); + priv->font_settings = NULL; + } + + if (priv->mail_settings != NULL) { + g_signal_handlers_disconnect_by_data (priv->mail_settings, object); + g_object_unref (priv->mail_settings); + priv->mail_settings = NULL; + } + + if (priv->web_extension_content_changed_cb_id > 0) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection (priv->web_extension), + priv->web_extension_content_changed_cb_id); + priv->web_extension_content_changed_cb_id = 0; + } + + if (priv->web_extension_selection_changed_cb_id > 0) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection (priv->web_extension), + priv->web_extension_selection_changed_cb_id); + priv->web_extension_selection_changed_cb_id = 0; + } + + if (priv->web_extension_undo_redo_state_changed_cb_id > 0) { + g_dbus_connection_signal_unsubscribe ( + g_dbus_proxy_get_connection (priv->web_extension), + priv->web_extension_undo_redo_state_changed_cb_id); + priv->web_extension_undo_redo_state_changed_cb_id = 0; + } + + if (priv->web_extension_watch_name_id > 0) { + g_bus_unwatch_name (priv->web_extension_watch_name_id); + priv->web_extension_watch_name_id = 0; + } + + if (priv->owner_change_clipboard_cb_id > 0) { + g_signal_handler_disconnect ( + gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + priv->owner_change_clipboard_cb_id); + priv->owner_change_clipboard_cb_id = 0; + } + + if (priv->owner_change_primary_clipboard_cb_id > 0) { + g_signal_handler_disconnect ( + gtk_clipboard_get (GDK_SELECTION_PRIMARY), + priv->owner_change_primary_clipboard_cb_id); + priv->owner_change_primary_clipboard_cb_id = 0; + } + + webkit_editor_finish_search (E_WEBKIT_EDITOR (object)); + + g_clear_object (&priv->web_extension); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_webkit_editor_parent_class)->dispose (object); +} + +static void +webkit_editor_finalize (GObject *object) +{ + EWebKitEditorPrivate *priv; + + priv = E_WEBKIT_EDITOR_GET_PRIVATE (object); + + if (priv->old_settings) { + g_hash_table_destroy (priv->old_settings); + priv->old_settings = NULL; + } + + if (priv->post_reload_operations) { + g_warn_if_fail (g_queue_is_empty (priv->post_reload_operations)); + + g_queue_free (priv->post_reload_operations); + priv->post_reload_operations = NULL; + } + + if (priv->background_color != NULL) { + gdk_rgba_free (priv->background_color); + priv->background_color = NULL; + } + + if (priv->font_color != NULL) { + gdk_rgba_free (priv->font_color); + priv->font_color = NULL; + } + + g_clear_object (&priv->spell_checker); + + g_free (priv->font_name); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_webkit_editor_parent_class)->finalize (object); +} + +static void +webkit_editor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CHANGED: + webkit_editor_set_changed ( + E_WEBKIT_EDITOR (object), + g_value_get_boolean (value)); + return; + + case PROP_EDITABLE: + webkit_editor_set_editable ( + E_WEBKIT_EDITOR (object), + g_value_get_boolean (value)); + return; + + case PROP_HTML_MODE: + webkit_editor_set_html_mode ( + E_WEBKIT_EDITOR (object), + g_value_get_boolean (value)); + return; + + case PROP_ALIGNMENT: + webkit_editor_set_alignment ( + E_WEBKIT_EDITOR (object), + g_value_get_enum (value)); + return; + + case PROP_BACKGROUND_COLOR: + webkit_editor_set_background_color ( + E_WEBKIT_EDITOR (object), + g_value_get_boxed (value)); + return; + + case PROP_BOLD: + webkit_editor_set_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_BOLD, + g_value_get_boolean (value), + "DOMSelectionSetBold"); + return; + + case PROP_FONT_COLOR: + webkit_editor_set_font_color ( + E_WEBKIT_EDITOR (object), + g_value_get_boxed (value)); + return; + + case PROP_BLOCK_FORMAT: + webkit_editor_set_block_format ( + E_WEBKIT_EDITOR (object), + g_value_get_enum (value)); + return; + + case PROP_FONT_NAME: + webkit_editor_set_font_name ( + E_WEBKIT_EDITOR (object), + g_value_get_string (value)); + return; + + case PROP_FONT_SIZE: + webkit_editor_set_font_size ( + E_WEBKIT_EDITOR (object), + g_value_get_int (value)); + return; + + case PROP_ITALIC: + webkit_editor_set_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_ITALIC, + g_value_get_boolean (value), + "DOMSelectionSetItalic"); + return; + + case PROP_MONOSPACED: + webkit_editor_set_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_MONOSPACE, + g_value_get_boolean (value), + "DOMSelectionSetMonospaced"); + return; + + case PROP_STRIKETHROUGH: + webkit_editor_set_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH, + g_value_get_boolean (value), + "DOMSelectionSetStrikethrough"); + return; + + case PROP_SUBSCRIPT: + webkit_editor_set_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT, + g_value_get_boolean (value), + "DOMSelectionSetSubscript"); + return; + + case PROP_SUPERSCRIPT: + webkit_editor_set_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT, + g_value_get_boolean (value), + "DOMSelectionSetSuperscript"); + return; + + case PROP_UNDERLINE: + webkit_editor_set_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_UNDERLINE, + g_value_get_boolean (value), + "DOMSelectionSetUnderline"); + return; + + case PROP_SPELL_CHECK_ENABLED: + webkit_editor_set_spell_check_enabled ( + E_WEBKIT_EDITOR (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +webkit_editor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_WEB_EXTENSION: + g_value_set_object ( + value, webkit_editor_get_web_extension ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_CAN_COPY: + g_value_set_boolean ( + value, webkit_editor_can_copy ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_CAN_CUT: + g_value_set_boolean ( + value, webkit_editor_can_cut ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_CAN_PASTE: + g_value_set_boolean ( + value, webkit_editor_can_paste ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_CAN_REDO: + g_value_set_boolean ( + value, webkit_editor_can_redo ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_CAN_UNDO: + g_value_set_boolean ( + value, webkit_editor_can_undo ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_CHANGED: + g_value_set_boolean ( + value, webkit_editor_get_changed ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_HTML_MODE: + g_value_set_boolean ( + value, webkit_editor_get_html_mode ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_EDITABLE: + g_value_set_boolean ( + value, webkit_editor_is_editable ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_ALIGNMENT: + g_value_set_enum ( + value, + webkit_editor_get_alignment ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_BACKGROUND_COLOR: + g_value_set_boxed ( + value, + webkit_editor_get_background_color ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_BLOCK_FORMAT: + g_value_set_enum ( + value, + webkit_editor_get_block_format ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_BOLD: + g_value_set_boolean ( + value, + webkit_editor_get_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_BOLD)); + return; + + case PROP_FONT_COLOR: + g_value_set_boxed ( + value, + webkit_editor_get_font_color ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_FONT_NAME: + g_value_set_string ( + value, + webkit_editor_get_font_name ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_FONT_SIZE: + g_value_set_int ( + value, + webkit_editor_get_font_size ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_INDENTED: + g_value_set_boolean ( + value, + webkit_editor_selection_is_indented ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_ITALIC: + g_value_set_boolean ( + value, + webkit_editor_get_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_ITALIC)); + return; + + case PROP_MONOSPACED: + g_value_set_boolean ( + value, + webkit_editor_get_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_MONOSPACE)); + return; + + case PROP_STRIKETHROUGH: + g_value_set_boolean ( + value, + webkit_editor_get_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH)); + return; + + case PROP_SUBSCRIPT: + g_value_set_boolean ( + value, + webkit_editor_get_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT)); + return; + + case PROP_SUPERSCRIPT: + g_value_set_boolean ( + value, + webkit_editor_get_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT)); + return; + + case PROP_UNDERLINE: + g_value_set_boolean ( + value, + webkit_editor_get_style_flag ( + E_WEBKIT_EDITOR (object), + E_CONTENT_EDITOR_STYLE_IS_UNDERLINE)); + return; + + case PROP_SPELL_CHECK_ENABLED: + g_value_set_boolean ( + value, + webkit_editor_get_spell_check_enabled ( + E_WEBKIT_EDITOR (object))); + return; + + case PROP_SPELL_CHECKER: + g_value_set_object ( + value, + webkit_editor_get_spell_checker ( + E_WEBKIT_EDITOR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +webkit_editor_move_caret_on_current_coordinates (GtkWidget *widget) +{ + gint x, y; + GdkDeviceManager *device_manager; + GdkDevice *pointer; + + device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget)); + pointer = gdk_device_manager_get_client_pointer (device_manager); + gdk_window_get_device_position ( + gtk_widget_get_window (widget), pointer, &x, &y, NULL); + webkit_editor_move_caret_on_coordinates + (E_CONTENT_EDITOR (widget), x, y, TRUE); +} + +static void +webkit_editor_settings_changed_cb (GSettings *settings, + const gchar *key, + EWebKitEditor *wk_editor) +{ + GVariant *new_value, *old_value; + + new_value = g_settings_get_value (settings, key); + old_value = g_hash_table_lookup (wk_editor->priv->old_settings, key); + + if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) { + if (new_value) + g_hash_table_insert (wk_editor->priv->old_settings, g_strdup (key), new_value); + else + g_hash_table_remove (wk_editor->priv->old_settings, key); + + webkit_editor_update_styles (E_CONTENT_EDITOR (wk_editor)); + } else if (new_value) { + g_variant_unref (new_value); + } +} + +static void +webkit_editor_load_changed_cb (EWebKitEditor *wk_editor, + WebKitLoadEvent load_event) +{ + wk_editor->priv->webkit_load_event = load_event; + + if (load_event != WEBKIT_LOAD_FINISHED) + return; + + if (wk_editor->priv->web_extension) + e_content_editor_emit_load_finished (E_CONTENT_EDITOR (wk_editor)); + else + wk_editor->priv->emit_load_finished_when_extension_is_ready = TRUE; + + wk_editor->priv->reload_in_progress = FALSE; + + dispatch_pending_operations (wk_editor); +} + +static void +webkit_editor_clipboard_owner_change_cb (GtkClipboard *clipboard, + GdkEventOwnerChange *event, + EWebKitEditor *wk_editor) +{ + if (!E_IS_WEBKIT_EDITOR (wk_editor)) + return; + + if (!wk_editor->priv->web_extension) + return; + + if (wk_editor->priv->copy_cut_actions_triggered && event->owner) + wk_editor->priv->copy_paste_clipboard_in_view = TRUE; + else + wk_editor->priv->copy_paste_clipboard_in_view = FALSE; + + if (wk_editor->priv->copy_paste_clipboard_in_view == wk_editor->priv->pasting_from_itself_extension_value) + return; + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "SetPastingContentFromItself", + g_variant_new ( + "(tb)", + current_page_id (wk_editor), + wk_editor->priv->copy_paste_clipboard_in_view), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + wk_editor->priv->copy_cut_actions_triggered = FALSE; + + wk_editor->priv->pasting_from_itself_extension_value = wk_editor->priv->copy_paste_clipboard_in_view; +} + +static void +webkit_editor_primary_clipboard_owner_change_cb (GtkClipboard *clipboard, + GdkEventOwnerChange *event, + EWebKitEditor *wk_editor) +{ + if (!E_IS_WEBKIT_EDITOR (wk_editor) || + !wk_editor->priv->web_extension) + return; + + if (!event->owner || !wk_editor->priv->can_copy) + wk_editor->priv->copy_paste_clipboard_in_view = FALSE; + + if (wk_editor->priv->copy_paste_clipboard_in_view == wk_editor->priv->pasting_from_itself_extension_value) + return; + + g_dbus_proxy_call ( + wk_editor->priv->web_extension, + "SetPastingContentFromItself", + g_variant_new ( + "(tb)", + current_page_id (wk_editor), + wk_editor->priv->copy_paste_clipboard_in_view), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + + wk_editor->priv->pasting_from_itself_extension_value = wk_editor->priv->copy_paste_clipboard_in_view; +} + +static gboolean +webkit_editor_paste_prefer_text_html (EWebKitEditor *wk_editor) +{ + if (wk_editor->priv->pasting_primary_clipboard) + return wk_editor->priv->copy_paste_primary_in_view; + else + return wk_editor->priv->copy_paste_clipboard_in_view; +} + +static void +webkit_editor_paste_clipboard_targets_cb (GtkClipboard *clipboard, + GdkAtom *targets, + gint n_targets, + EWebKitEditor *wk_editor) +{ + if (targets == NULL || n_targets < 0) + return; + + /* If view doesn't have focus, focus it */ + if (!gtk_widget_has_focus (GTK_WIDGET (wk_editor))) + gtk_widget_grab_focus (GTK_WIDGET (wk_editor)); + + /* Order is important here to ensure common use cases are + * handled correctly. See GNOME bug #603715 for details. */ + /* Prefer plain text over HTML when in the plain text mode, but only + * when pasting content from outside the editor view. */ + if (wk_editor->priv->html_mode || + webkit_editor_paste_prefer_text_html (wk_editor)) { + gchar *content = NULL; + + if (e_targets_include_html (targets, n_targets)) { + if (!(content = e_clipboard_wait_for_html (clipboard))) + return; + + webkit_editor_insert_content ( + E_CONTENT_EDITOR (wk_editor), + content, + E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + g_free (content); + return; + } + + if (gtk_targets_include_text (targets, n_targets)) { + if (!(content = gtk_clipboard_wait_for_text (clipboard))) + return; + + webkit_editor_insert_content ( + E_CONTENT_EDITOR (wk_editor), + content, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN | + E_CONTENT_EDITOR_INSERT_CONVERT); + + g_free (content); + return; + } + } else { + gchar *content = NULL; + + if (gtk_targets_include_text (targets, n_targets)) { + if (!(content = gtk_clipboard_wait_for_text (clipboard))) + return; + + webkit_editor_insert_content ( + E_CONTENT_EDITOR (wk_editor), + content, + E_CONTENT_EDITOR_INSERT_TEXT_PLAIN | + E_CONTENT_EDITOR_INSERT_CONVERT); + + g_free (content); + return; + } + + if (e_targets_include_html (targets, n_targets)) { + if (!(content = e_clipboard_wait_for_html (clipboard))) + return; + + webkit_editor_insert_content ( + E_CONTENT_EDITOR (wk_editor), + content, + E_CONTENT_EDITOR_INSERT_TEXT_HTML); + + g_free (content); + return; + } + } + + if (gtk_targets_include_image (targets, n_targets, TRUE)) { + gchar *uri; + + if (!(uri = e_util_save_image_from_clipboard (clipboard))) + return; + + webkit_editor_insert_image (E_CONTENT_EDITOR (wk_editor), uri); + + g_free (uri); + + return; + } +} + +static void +webkit_editor_paste_primary (EContentEditor *editor) +{ + + GtkClipboard *clipboard; + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + /* Remember, that we are pasting primary clipboard to return + * correct value in e_html_editor_view_is_pasting_content_from_itself. */ + wk_editor->priv->pasting_primary_clipboard = TRUE; + + webkit_editor_move_caret_on_current_coordinates (GTK_WIDGET (wk_editor)); + + clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); + + gtk_clipboard_request_targets ( + clipboard, (GtkClipboardTargetsReceivedFunc) + webkit_editor_paste_clipboard_targets_cb, wk_editor); +} + +static void +webkit_editor_paste (EContentEditor *editor) +{ + GtkClipboard *clipboard; + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (editor); + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + gtk_clipboard_request_targets ( + clipboard, (GtkClipboardTargetsReceivedFunc) + webkit_editor_paste_clipboard_targets_cb, wk_editor); +} + +static void +webkit_editor_mouse_target_changed_cb (EWebKitEditor *wk_editor, + WebKitHitTestResult *hit_test_result, + guint modifiers, + gpointer user_data) +{ + /* Ctrl + Left Click on link opens it. */ + if (webkit_hit_test_result_context_is_link (hit_test_result) && + (modifiers & GDK_CONTROL_MASK)) { + GdkScreen *screen; + const gchar *uri; + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (wk_editor)); + screen = gtk_window_get_screen (GTK_WINDOW (toplevel)); + + uri = webkit_hit_test_result_get_link_uri (hit_test_result); + + gtk_show_uri (screen, uri, GDK_CURRENT_TIME, NULL); + } +} + +static gboolean +webkit_editor_context_menu_cb (EWebKitEditor *wk_editor, + WebKitContextMenu *context_menu, + GdkEvent *event, + WebKitHitTestResult *hit_test_result) +{ + GVariant *result; + EContentEditorNodeFlags flags = 0; + gboolean handled; + + webkit_context_menu_remove_all (context_menu); + + if ((result = webkit_context_menu_get_user_data (context_menu))) + flags = g_variant_get_int32 (result); + + handled = e_content_editor_emit_context_menu_requested (E_CONTENT_EDITOR (wk_editor), flags, event); + + return handled; +} + +static void +webkit_editor_drag_end_cb (EWebKitEditor *wk_editor, + GdkDragContext *context) +{ + webkit_editor_call_simple_extension_function (wk_editor, "DOMDragAndDropEnd"); +} + +static void +webkit_editor_web_process_crashed_cb (EWebKitEditor *wk_editor) +{ + g_warning ( + "WebKitWebProcess (page id %ld) for EWebKitEditor crashed", + webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor))); + + wk_editor->priv->web_extension_selection_changed_cb_id = 0; + wk_editor->priv->web_extension_content_changed_cb_id = 0; + wk_editor->priv->web_extension_undo_redo_state_changed_cb_id = 0; +} + +static gboolean +webkit_editor_button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + if (event->button == 2) { + if (!e_content_editor_emit_paste_primary_clipboard (E_CONTENT_EDITOR (widget))) + webkit_editor_paste_primary (E_CONTENT_EDITOR( (widget))); + + return TRUE; + } + + /* Chain up to parent's button_press_event() method. */ + return GTK_WIDGET_CLASS (e_webkit_editor_parent_class)->button_press_event (widget, event); +} + +static gboolean +webkit_editor_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + EWebKitEditor *wk_editor; + + wk_editor = E_WEBKIT_EDITOR (widget); + + if ((((event)->state & GDK_SHIFT_MASK) && + ((event)->keyval == GDK_KEY_Insert)) || + (((event)->state & GDK_CONTROL_MASK) && + ((event)->keyval == GDK_KEY_v))) { + if (!e_content_editor_emit_paste_clipboard (E_CONTENT_EDITOR (widget))) + webkit_editor_paste (E_CONTENT_EDITOR (widget)); + + return TRUE; + } + + if (((event)->state & GDK_CONTROL_MASK) && + ((event)->keyval == GDK_KEY_Insert)) { + webkit_editor_copy (E_CONTENT_EDITOR (wk_editor)); + return TRUE; + } + + if (((event)->state & GDK_CONTROL_MASK) && + ((event)->keyval == GDK_KEY_z)) { + webkit_editor_undo (E_CONTENT_EDITOR (wk_editor)); + return TRUE; + } + + if (((event)->state & (GDK_CONTROL_MASK)) && + ((event)->keyval == GDK_KEY_Z)) { + webkit_editor_redo (E_CONTENT_EDITOR (wk_editor)); + return TRUE; + } + + if (((event)->state & GDK_SHIFT_MASK) && + ((event)->keyval == GDK_KEY_Delete)) { + webkit_editor_cut (E_CONTENT_EDITOR (wk_editor)); + return TRUE; + } + + if (((event)->state & GDK_CONTROL_MASK) && + ((event)->state & GDK_SHIFT_MASK) && + ((event)->keyval == GDK_KEY_I)) { + webkit_editor_show_inspector (wk_editor); + return TRUE; + } + + /* Chain up to parent's key_press_event() method. */ + return GTK_WIDGET_CLASS (e_webkit_editor_parent_class)->key_press_event (widget, event); +} + +static void +e_webkit_editor_class_init (EWebKitEditorClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + g_type_class_add_private (class, sizeof (EWebKitEditorPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = webkit_editor_constructed; + object_class->constructor = webkit_editor_constructor; + object_class->get_property = webkit_editor_get_property; + object_class->set_property = webkit_editor_set_property; + object_class->dispose = webkit_editor_dispose; + object_class->finalize = webkit_editor_finalize; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->button_press_event = webkit_editor_button_press_event; + widget_class->key_press_event = webkit_editor_key_press_event; + + g_object_class_install_property ( + object_class, + PROP_WEB_EXTENSION, + g_param_spec_object ( + "web-extension", + "Web Extension", + "The Web Extension to use to talk to the WebProcess", + G_TYPE_DBUS_PROXY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_override_property ( + object_class, PROP_CAN_COPY, "can-copy"); + g_object_class_override_property ( + object_class, PROP_CAN_CUT, "can-cut"); + g_object_class_override_property ( + object_class, PROP_CAN_PASTE, "can-paste"); + g_object_class_override_property ( + object_class, PROP_CAN_REDO, "can-redo"); + g_object_class_override_property ( + object_class, PROP_CAN_UNDO, "can-undo"); + g_object_class_override_property ( + object_class, PROP_CHANGED, "changed"); + g_object_class_override_property ( + object_class, PROP_HTML_MODE, "html-mode"); + g_object_class_override_property ( + object_class, PROP_EDITABLE, "editable"); + g_object_class_override_property ( + object_class, PROP_ALIGNMENT, "alignment"); + g_object_class_override_property ( + object_class, PROP_BACKGROUND_COLOR, "background-color"); + g_object_class_override_property ( + object_class, PROP_BLOCK_FORMAT, "block-format"); + g_object_class_override_property ( + object_class, PROP_BOLD, "bold"); + g_object_class_override_property ( + object_class, PROP_FONT_COLOR, "font-color"); + g_object_class_override_property ( + object_class, PROP_FONT_NAME, "font-name"); + g_object_class_override_property ( + object_class, PROP_FONT_SIZE, "font-size"); + g_object_class_override_property ( + object_class, PROP_INDENTED, "indented"); + g_object_class_override_property ( + object_class, PROP_ITALIC, "italic"); + g_object_class_override_property ( + object_class, PROP_MONOSPACED, "monospaced"); + g_object_class_override_property ( + object_class, PROP_STRIKETHROUGH, "strikethrough"); + g_object_class_override_property ( + object_class, PROP_SUBSCRIPT, "subscript"); + g_object_class_override_property ( + object_class, PROP_SUPERSCRIPT, "superscript"); + g_object_class_override_property ( + object_class, PROP_UNDERLINE, "underline"); + g_object_class_override_property ( + object_class, PROP_SPELL_CHECK_ENABLED, "spell-check-enabled"); + g_object_class_override_property ( + object_class, PROP_SPELL_CHECKER, "spell-checker"); +} + +static void +e_webkit_editor_init (EWebKitEditor *wk_editor) +{ + GSettings *g_settings; + GSettingsSchema *settings_schema; + + wk_editor->priv = E_WEBKIT_EDITOR_GET_PRIVATE (wk_editor); + + wk_editor->priv->spell_check_enabled = TRUE; + wk_editor->priv->spell_checker = e_spell_checker_new (); + wk_editor->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); + + webkit_editor_watch_web_extension (wk_editor); + + g_signal_connect ( + wk_editor, "load-changed", + G_CALLBACK (webkit_editor_load_changed_cb), NULL); + + g_signal_connect ( + wk_editor, "context-menu", + G_CALLBACK (webkit_editor_context_menu_cb), NULL); + + g_signal_connect ( + wk_editor, "mouse-target-changed", + G_CALLBACK (webkit_editor_mouse_target_changed_cb), NULL); + + g_signal_connect ( + wk_editor, "drag-end", + G_CALLBACK (webkit_editor_drag_end_cb), NULL); + + g_signal_connect ( + wk_editor, "web-process-crashed", + G_CALLBACK (webkit_editor_web_process_crashed_cb), NULL); + + wk_editor->priv->owner_change_primary_clipboard_cb_id = g_signal_connect ( + gtk_clipboard_get (GDK_SELECTION_PRIMARY), "owner-change", + G_CALLBACK (webkit_editor_primary_clipboard_owner_change_cb), wk_editor); + + wk_editor->priv->owner_change_clipboard_cb_id = g_signal_connect ( + gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), "owner-change", + G_CALLBACK (webkit_editor_clipboard_owner_change_cb), wk_editor); + + g_settings = e_util_ref_settings ("org.gnome.desktop.interface"); + g_signal_connect ( + g_settings, "changed::font-name", + G_CALLBACK (webkit_editor_settings_changed_cb), wk_editor); + g_signal_connect ( + g_settings, "changed::monospace-font-name", + G_CALLBACK (webkit_editor_settings_changed_cb), wk_editor); + wk_editor->priv->font_settings = g_settings; + + g_settings = e_util_ref_settings ("org.gnome.evolution.mail"); + wk_editor->priv->mail_settings = g_settings; + + /* This schema is optional. Use if available. */ + settings_schema = g_settings_schema_source_lookup ( + g_settings_schema_source_get_default (), + "org.gnome.settings-daemon.plugins.xsettings", FALSE); + if (settings_schema != NULL) { + g_settings = e_util_ref_settings ("org.gnome.settings-daemon.plugins.xsettings"); + g_signal_connect ( + g_settings, "changed::antialiasing", + G_CALLBACK (webkit_editor_settings_changed_cb), wk_editor); + wk_editor->priv->aliasing_settings = g_settings; + } + + wk_editor->priv->html_mode = TRUE; + wk_editor->priv->changed = FALSE; + wk_editor->priv->can_copy = FALSE; + wk_editor->priv->can_cut = FALSE; + wk_editor->priv->can_paste = FALSE; + wk_editor->priv->can_undo = FALSE; + wk_editor->priv->can_redo = FALSE; + wk_editor->priv->copy_paste_clipboard_in_view = FALSE; + wk_editor->priv->copy_paste_primary_in_view = FALSE; + wk_editor->priv->copy_cut_actions_triggered = FALSE; + wk_editor->priv->pasting_primary_clipboard = FALSE; + wk_editor->priv->pasting_from_itself_extension_value = FALSE; + wk_editor->priv->current_user_stylesheet = NULL; + wk_editor->priv->emit_load_finished_when_extension_is_ready = FALSE; + + wk_editor->priv->font_color = gdk_rgba_copy (&black); + wk_editor->priv->background_color = gdk_rgba_copy (&white); + wk_editor->priv->font_name = NULL; + wk_editor->priv->font_size = E_CONTENT_EDITOR_FONT_SIZE_NORMAL; + wk_editor->priv->block_format = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH; + wk_editor->priv->alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + + wk_editor->priv->web_extension_selection_changed_cb_id = 0; + wk_editor->priv->web_extension_content_changed_cb_id = 0; + wk_editor->priv->web_extension_undo_redo_state_changed_cb_id = 0; +} + +static void +e_webkit_editor_content_editor_init (EContentEditorInterface *iface) +{ + iface->initialize = webkit_editor_initialize; + iface->update_styles = webkit_editor_update_styles; + iface->insert_content = webkit_editor_insert_content; + iface->get_content = webkit_editor_get_content; + iface->insert_image = webkit_editor_insert_image; + iface->insert_image_from_mime_part = webkit_editor_insert_image_from_mime_part; + iface->insert_emoticon = webkit_editor_insert_emoticon; + iface->move_caret_on_coordinates = webkit_editor_move_caret_on_coordinates; + iface->cut = webkit_editor_cut; + iface->copy = webkit_editor_copy; + iface->paste = webkit_editor_paste; + iface->paste_primary = webkit_editor_paste_primary; + iface->undo = webkit_editor_undo; + iface->redo = webkit_editor_redo; + iface->clear_undo_redo_history = webkit_editor_clear_undo_redo_history; + iface->set_spell_checking_languages = webkit_editor_set_spell_checking_languages; + /* FIXME WK2 iface->get_selected_text = webkit_editor_get_selected_text; */ + iface->get_caret_word = webkit_editor_get_caret_word; + iface->replace_caret_word = webkit_editor_replace_caret_word; + iface->select_all = webkit_editor_select_all; + iface->selection_indent = webkit_editor_selection_indent; + iface->selection_unindent = webkit_editor_selection_unindent; + /* FIXME WK2 iface->create_link = webkit_editor_create_link; */ + iface->selection_unlink = webkit_editor_selection_unlink; + iface->find = webkit_editor_find; + iface->replace = webkit_editor_replace; + iface->replace_all = webkit_editor_replace_all; + iface->selection_save = webkit_editor_selection_save; + iface->selection_restore = webkit_editor_selection_restore; + iface->selection_wrap = webkit_editor_selection_wrap; + iface->get_caret_position = webkit_editor_get_caret_position; + iface->get_caret_offset = webkit_editor_get_caret_offset; + iface->get_current_signature_uid = webkit_editor_get_current_signature_uid; + iface->is_ready = webkit_editor_is_ready; + iface->insert_signature = webkit_editor_insert_signature; + iface->delete_cell_contents = webkit_editor_delete_cell_contents; + iface->delete_column = webkit_editor_delete_column; + iface->delete_row = webkit_editor_delete_row; + iface->delete_table = webkit_editor_delete_table; + iface->insert_column_after = webkit_editor_insert_column_after; + iface->insert_column_before = webkit_editor_insert_column_before; + iface->insert_row_above = webkit_editor_insert_row_above; + iface->insert_row_below = webkit_editor_insert_row_below; + iface->on_h_rule_dialog_open = webkit_editor_on_h_rule_dialog_open; + iface->on_h_rule_dialog_close = webkit_editor_on_h_rule_dialog_close; + iface->h_rule_set_align = webkit_editor_h_rule_set_align; + iface->h_rule_get_align = webkit_editor_h_rule_get_align; + iface->h_rule_set_size = webkit_editor_h_rule_set_size; + iface->h_rule_get_size = webkit_editor_h_rule_get_size; + iface->h_rule_set_width = webkit_editor_h_rule_set_width; + iface->h_rule_get_width = webkit_editor_h_rule_get_width; + iface->h_rule_set_no_shade = webkit_editor_h_rule_set_no_shade; + iface->h_rule_get_no_shade = webkit_editor_h_rule_get_no_shade; + iface->on_image_dialog_open = webkit_editor_on_image_dialog_open; + iface->on_image_dialog_close = webkit_editor_on_image_dialog_close; + iface->image_set_src = webkit_editor_image_set_src; + iface->image_get_src = webkit_editor_image_get_src; + iface->image_set_alt = webkit_editor_image_set_alt; + iface->image_get_alt = webkit_editor_image_get_alt; + iface->image_set_url = webkit_editor_image_set_url; + iface->image_get_url = webkit_editor_image_get_url; + iface->image_set_vspace = webkit_editor_image_set_vspace; + iface->image_get_vspace = webkit_editor_image_get_vspace; + iface->image_set_hspace = webkit_editor_image_set_hspace; + iface->image_get_hspace = webkit_editor_image_get_hspace; + iface->image_set_border = webkit_editor_image_set_border; + iface->image_get_border = webkit_editor_image_get_border; + iface->image_set_align = webkit_editor_image_set_align; + iface->image_get_align = webkit_editor_image_get_align; + iface->image_get_natural_width = webkit_editor_image_get_natural_width; + iface->image_get_natural_height = webkit_editor_image_get_natural_height; + iface->image_set_height = webkit_editor_image_set_height; + iface->image_set_width = webkit_editor_image_set_width; + iface->image_set_height_follow = webkit_editor_image_set_height_follow; + iface->image_set_width_follow = webkit_editor_image_set_width_follow; + iface->image_get_width = webkit_editor_image_get_width; + iface->image_get_height = webkit_editor_image_get_height; + iface->on_link_dialog_open = webkit_editor_on_link_dialog_open; + iface->on_link_dialog_close = webkit_editor_on_link_dialog_close; + iface->link_set_values = webkit_editor_link_set_values; + iface->link_get_values = webkit_editor_link_get_values; + iface->page_set_text_color = webkit_editor_page_set_text_color; + iface->page_get_text_color = webkit_editor_page_get_text_color; + iface->page_set_background_color = webkit_editor_page_set_background_color; + iface->page_get_background_color = webkit_editor_page_get_background_color; + iface->page_set_link_color = webkit_editor_page_set_link_color; + iface->page_get_link_color = webkit_editor_page_get_link_color; + iface->page_set_visited_link_color = webkit_editor_page_set_visited_link_color; + iface->page_get_visited_link_color = webkit_editor_page_get_visited_link_color; + iface->page_set_background_image_uri = webkit_editor_page_set_background_image_uri; + iface->page_get_background_image_uri = webkit_editor_page_get_background_image_uri; + iface->on_page_dialog_open = webkit_editor_on_page_dialog_open; + iface->on_page_dialog_close = webkit_editor_on_page_dialog_close; + iface->on_cell_dialog_open = webkit_editor_on_cell_dialog_open; + iface->on_cell_dialog_close = webkit_editor_on_cell_dialog_close; + iface->cell_set_v_align = webkit_editor_cell_set_v_align; + iface->cell_get_v_align = webkit_editor_cell_get_v_align; + iface->cell_set_align = webkit_editor_cell_set_align; + iface->cell_get_align = webkit_editor_cell_get_align; + iface->cell_set_wrap = webkit_editor_cell_set_wrap; + iface->cell_get_wrap = webkit_editor_cell_get_wrap; + iface->cell_set_header_style = webkit_editor_cell_set_header_style; + iface->cell_is_header = webkit_editor_cell_is_header; + iface->cell_get_width = webkit_editor_cell_get_width; + iface->cell_set_width = webkit_editor_cell_set_width; + iface->cell_get_row_span = webkit_editor_cell_get_row_span; + iface->cell_set_row_span = webkit_editor_cell_set_row_span; + iface->cell_get_col_span = webkit_editor_cell_get_col_span; + iface->cell_set_col_span = webkit_editor_cell_set_col_span; + iface->cell_get_background_image_uri = webkit_editor_cell_get_background_image_uri; + iface->cell_set_background_image_uri = webkit_editor_cell_set_background_image_uri; + iface->cell_get_background_color = webkit_editor_cell_get_background_color; + iface->cell_set_background_color = webkit_editor_cell_set_background_color; + iface->table_set_row_count = webkit_editor_table_set_row_count; + iface->table_get_row_count = webkit_editor_table_get_row_count; + iface->table_set_column_count = webkit_editor_table_set_column_count; + iface->table_get_column_count = webkit_editor_table_get_column_count; + iface->table_set_width = webkit_editor_table_set_width; + iface->table_get_width = webkit_editor_table_get_width; + iface->table_set_align = webkit_editor_table_set_align; + iface->table_get_align = webkit_editor_table_get_align; + iface->table_set_padding = webkit_editor_table_set_padding; + iface->table_get_padding = webkit_editor_table_get_padding; + iface->table_set_spacing = webkit_editor_table_set_spacing; + iface->table_get_spacing = webkit_editor_table_get_spacing; + iface->table_set_border = webkit_editor_table_set_border; + iface->table_get_border = webkit_editor_table_get_border; + iface->table_get_background_image_uri = webkit_editor_table_get_background_image_uri; + iface->table_set_background_image_uri = webkit_editor_table_set_background_image_uri; + iface->table_get_background_color = webkit_editor_table_get_background_color; + iface->table_set_background_color = webkit_editor_table_set_background_color; + iface->on_table_dialog_open = webkit_editor_on_table_dialog_open; + iface->on_table_dialog_close = webkit_editor_on_table_dialog_close; + iface->on_spell_check_dialog_open = webkit_editor_on_spell_check_dialog_open; + iface->on_spell_check_dialog_close = webkit_editor_on_spell_check_dialog_close; + iface->spell_check_next_word = webkit_editor_spell_check_next_word; + iface->spell_check_prev_word = webkit_editor_spell_check_prev_word; + iface->on_replace_dialog_open = webkit_editor_on_replace_dialog_open; + iface->on_replace_dialog_close = webkit_editor_on_replace_dialog_close; + iface->on_find_dialog_open = webkit_editor_on_find_dialog_open; + iface->on_find_dialog_close = webkit_editor_on_find_dialog_close; +} diff --git a/modules/webkit-editor/e-webkit-editor.h b/modules/webkit-editor/e-webkit-editor.h new file mode 100644 index 0000000..3b21679 --- /dev/null +++ b/modules/webkit-editor/e-webkit-editor.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef E_WEBKIT_EDITOR_H +#define E_WEBKIT_EDITOR_H + +#include <webkit2/webkit2.h> + +/* Standard GObject macros */ +#define E_TYPE_WEBKIT_EDITOR \ + (e_webkit_editor_get_type ()) +#define E_WEBKIT_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_WEBKIT_EDITOR, EWebKitEditor)) +#define E_WEBKIT_EDITOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_WEBKIT_EDITOR, EWebKitEditorClass)) +#define E_IS_WEBKIT_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_WEBKIT_EDITOR)) +#define E_IS_WEBKIT_EDITOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_WEBKIT_EDITOR)) +#define E_WEBKIT_EDITOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_WEBKIT_EDITOR, EWebKitEditorClass)) + +G_BEGIN_DECLS + +typedef struct _EWebKitEditor EWebKitEditor; +typedef struct _EWebKitEditorClass EWebKitEditorClass; +typedef struct _EWebKitEditorPrivate EWebKitEditorPrivate; + +struct _EWebKitEditor { + WebKitWebView parent; + EWebKitEditorPrivate *priv; +}; + +struct _EWebKitEditorClass { + WebKitWebViewClass parent_class; + + gboolean (*popup_event) (EWebKitEditor *wk_editor, + GdkEventButton *event); + void (*paste_primary_clipboard) + (EWebKitEditor *wk_editor); +}; + +GType e_webkit_editor_get_type (void) G_GNUC_CONST; + +EWebKitEditor * + e_webkit_editor_new (void); + +G_END_DECLS + +#endif /* E_WEBKIT_EDITOR_H */ diff --git a/modules/webkit-editor/evolution-module-webkit-editor.c b/modules/webkit-editor/evolution-module-webkit-editor.c new file mode 100644 index 0000000..116903f --- /dev/null +++ b/modules/webkit-editor/evolution-module-webkit-editor.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-webkit-editor-extension.h" + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + e_webkit_editor_extension_type_register (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} diff --git a/modules/webkit-editor/web-extension/Makefile.am b/modules/webkit-editor/web-extension/Makefile.am new file mode 100644 index 0000000..f2a7b02 --- /dev/null +++ b/modules/webkit-editor/web-extension/Makefile.am @@ -0,0 +1,38 @@ +webextensionswebkiteditor_LTLIBRARIES = libewebkiteditorwebextension.la + +libewebkiteditorwebextension_la_SOURCES = \ + e-composer-dom-functions.c \ + e-composer-dom-functions.h \ + e-dialogs-dom-functions.c \ + e-dialogs-dom-functions.h \ + e-editor-dom-functions.c \ + e-editor-dom-functions.h \ + e-editor-page.c \ + e-editor-page.h \ + e-editor-undo-redo-manager.c \ + e-editor-undo-redo-manager.h \ + e-editor-web-extension.c \ + e-editor-web-extension.h \ + e-editor-web-extension-main.c \ + e-editor-web-extension-names.h \ + $(NULL) + +libewebkiteditorwebextension_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(WEB_EXTENSIONS_CFLAGS) + +libewebkiteditorwebextension_la_LIBADD = \ + $(top_builddir)/e-util/libevolution-util.la \ + $(top_builddir)/web-extensions/libedomutils.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(WEB_EXTENSIONS_LIBS) + +libewebkiteditorwebextension_la_LDFLAGS = \ + -module -avoid-version -no-undefined + +-include $(top_srcdir)/git.mk diff --git a/modules/webkit-editor/web-extension/e-composer-dom-functions.c b/modules/webkit-editor/web-extension/e-composer-dom-functions.c new file mode 100644 index 0000000..9c8a4fa --- /dev/null +++ b/modules/webkit-editor/web-extension/e-composer-dom-functions.c @@ -0,0 +1,832 @@ +/* + * e-composer-private-dom-functions.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#define WEBKIT_DOM_USE_UNSTABLE_API +#include <webkitdom/WebKitDOMDOMSelection.h> +#include <webkitdom/WebKitDOMDOMWindowUnstable.h> +#include <webkitdom/WebKitDOMHTMLElementUnstable.h> +#undef WEBKIT_DOM_USE_UNSTABLE_API + +#include <camel/camel.h> + +#include "web-extensions/e-dom-utils.h" + +#include "e-editor-page.h" +#include "e-editor-dom-functions.h" +#include "e-editor-undo-redo-manager.h" + +#include "e-composer-dom-functions.h" + +static WebKitDOMElement * +prepare_top_signature_spacer (EEditorPage *editor_page) +{ + WebKitDOMElement *element; + + element = e_editor_dom_prepare_paragraph (editor_page, FALSE); + webkit_dom_element_remove_attribute (element, "id"); + element_add_class (element, "-x-evo-top-signature-spacer"); + + return element; +} + +static gboolean +add_signature_delimiter (void) +{ + gboolean ret_val; + GSettings *settings; + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + ret_val = !g_settings_get_boolean (settings, "composer-no-signature-delim"); + g_object_unref (settings); + + return ret_val; +} + +static gboolean +use_top_signature (void) +{ + gboolean ret_val; + GSettings *settings; + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + ret_val = g_settings_get_boolean (settings, "composer-top-signature"); + g_object_unref (settings); + + return ret_val; +} + +static gboolean +start_typing_at_bottom (void) +{ + gboolean ret_val; + GSettings *settings; + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + ret_val = g_settings_get_boolean (settings, "composer-reply-start-bottom"); + g_object_unref (settings); + + return ret_val; +} + +static void +move_caret_after_signature_inserted (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element, *signature; + WebKitDOMHTMLElement *body; + WebKitDOMNodeList *paragraphs = NULL; + gboolean top_signature; + gboolean start_bottom; + gboolean has_paragraphs_in_body = TRUE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + top_signature = use_top_signature (); + start_bottom = start_typing_at_bottom (); + + body = webkit_dom_document_get_body (document); + e_editor_page_block_selection_changed (editor_page); + + paragraphs = webkit_dom_document_query_selector_all (document, "[data-evo-paragraph]", NULL); + signature = webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL); + /* Situation when wrapped paragraph is just in signature and not in message body */ + if (webkit_dom_node_list_get_length (paragraphs) == 1) + if (signature && webkit_dom_element_query_selector (signature, "[data-evo-paragraph]", NULL)) + has_paragraphs_in_body = FALSE; + + /* + * + * Keeping Signatures in the beginning of composer + * ------------------------------------------------ + * + * Purists are gonna blast me for this. + * But there are so many people (read Outlook users) who want this. + * And Evo is an exchange-client, Outlook-replacement etc. + * So Here it goes :( + * + * -- Sankar + * + */ + if (signature && top_signature) { + WebKitDOMElement *spacer; + + spacer = prepare_top_signature_spacer (editor_page); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (spacer), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (signature)), + NULL); + } + + if (webkit_dom_node_list_get_length (paragraphs) == 0) + has_paragraphs_in_body = FALSE; + + element = webkit_dom_document_get_element_by_id (document, "-x-evo-input-start"); + if (!signature) { + if (start_bottom) { + if (!element) { + element = e_editor_dom_prepare_paragraph (editor_page, FALSE); + webkit_dom_element_set_id (element, "-x-evo-input-start"); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (element), + NULL); + } + } else + element = WEBKIT_DOM_ELEMENT (body); + + goto move_caret; + } + + /* When there is an option composer-reply-start-bottom set we have + * to move the caret between reply and signature. */ + if (!has_paragraphs_in_body) { + element = e_editor_dom_prepare_paragraph (editor_page, FALSE); + webkit_dom_element_set_id (element, "-x-evo-input-start"); + if (top_signature) { + if (start_bottom) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (element), + NULL); + } else { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (signature), + NULL); + } + } else { + if (start_bottom) + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (signature), + NULL); + else + element = WEBKIT_DOM_ELEMENT (body); + } + } else { + if (!element && top_signature) { + element = e_editor_dom_prepare_paragraph (editor_page, FALSE); + webkit_dom_element_set_id (element, "-x-evo-input-start"); + if (start_bottom) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (element), + NULL); + } else { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (signature), + NULL); + } + } else if (element && top_signature && !start_bottom) { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (signature), + NULL); + } else if (element && start_bottom) { + /* Leave it how it is */ + } else + element = WEBKIT_DOM_ELEMENT (body); + } + + move_caret: + if (element) { + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMRange *range = NULL; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + range = webkit_dom_document_create_range (document); + + webkit_dom_range_select_node_contents ( + range, WEBKIT_DOM_NODE (element), NULL); + webkit_dom_range_collapse (range, TRUE, NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + + g_clear_object (&dom_selection); + g_clear_object (&dom_window); + g_clear_object (&range); + } + + if (start_bottom) + e_editor_dom_scroll_to_caret (editor_page); + + g_clear_object (¶graphs); + + e_editor_dom_force_spell_check_in_viewport (editor_page); + e_editor_page_unblock_selection_changed (editor_page); +} + +gchar * +e_composer_dom_insert_signature (EEditorPage *editor_page, + const gchar *content, + gboolean is_html, + const gchar *id, + gboolean *set_signature_from_message, + gboolean *check_if_signature_is_changed, + gboolean *ignore_next_signature_change) +{ + WebKitDOMDocument *document; + WebKitDOMElement *signature_to_insert; + WebKitDOMElement *insert_signature_in = NULL; + WebKitDOMElement *signature_wrapper = NULL; + WebKitDOMElement *element, *converted_signature = NULL; + WebKitDOMHTMLElement *body; + WebKitDOMHTMLCollection *signatures = NULL; + gchar *new_signature_id = NULL; + gchar *signature_text = NULL; + gboolean top_signature, html_mode; + gulong list_length, ii; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + g_return_val_if_fail (set_signature_from_message != NULL, NULL); + g_return_val_if_fail (check_if_signature_is_changed != NULL, NULL); + g_return_val_if_fail (ignore_next_signature_change != NULL, NULL); + + document = e_editor_page_get_document (editor_page); + body = webkit_dom_document_get_body (document); + + /* "Edit as New Message" sets is_message_from_edit_as_new. + * Always put the signature at the bottom for that case. */ + top_signature = use_top_signature (); + + html_mode = e_editor_page_get_html_mode (editor_page); + + /* Create the DOM signature that is the same across all types of signatures. */ + signature_to_insert = webkit_dom_document_create_element (document, "span", NULL); + webkit_dom_element_set_class_name (signature_to_insert, "-x-evo-signature"); + /* The combo box active ID is the signature's ESource UID. */ + webkit_dom_element_set_id (signature_to_insert, id); + insert_signature_in = signature_to_insert; + + /* The signature has no content usually it means it is set to None. */ + if (!(content && *content)) + goto insert; + + if (!is_html) { + gchar *html; + + html = camel_text_to_html (content, 0, 0); + if (html) { + signature_text = html; + } else + signature_text = g_strdup (content); + + insert_signature_in = webkit_dom_document_create_element (document, "pre", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (signature_to_insert), + WEBKIT_DOM_NODE (insert_signature_in), + NULL); + } else + signature_text = g_strdup (content); + + /* If inserting HTML signature in the plain text composer we have to convert it. */ + if (is_html && !html_mode && !strstr (signature_text, "data-evo-signature-plain-text-mode")) { + gchar *inner_text; + + /* Save the converted signature to avoid parsing it later again + * while inserting it into the view. */ + converted_signature = webkit_dom_document_create_element (document, "pre", NULL); + webkit_dom_element_set_inner_html (converted_signature, signature_text, NULL); + e_editor_dom_convert_element_from_html_to_plain_text (editor_page, converted_signature); + inner_text = webkit_dom_html_element_get_inner_text (WEBKIT_DOM_HTML_ELEMENT (converted_signature)); + + g_free (signature_text); + signature_text = inner_text ? g_strstrip (inner_text) : g_strdup (""); + /* because of the -- \n check */ + is_html = FALSE; + } + + /* The signature dash convention ("-- \n") is specified + * in the "Son of RFC 1036", section 4.3.2. + * http://www.chemie.fu-berlin.de/outerspace/netnews/son-of-1036.html + */ + if (add_signature_delimiter ()) { + const gchar *delim; + const gchar *delim_nl; + + if (is_html) { + delim = "-- <BR>"; + delim_nl = "\n-- <BR>"; + } else { + delim = "-- \n"; + delim_nl = "\n-- \n"; + } + + /* Skip the delimiter if the signature already has one. */ + if (g_ascii_strncasecmp (signature_text, delim, strlen (delim)) == 0) + ; /* skip */ + else if (e_util_strstrcase (signature_text, delim_nl) != NULL) + ; /* skip */ + else { + WebKitDOMElement *pre_delimiter; + + pre_delimiter = webkit_dom_document_create_element (document, "pre", NULL); + /* Always use the HTML delimiter as we are never in anything + * like a strict plain text mode. */ + webkit_dom_element_set_inner_html (pre_delimiter, "-- <br>", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (insert_signature_in), + WEBKIT_DOM_NODE (pre_delimiter), + NULL); + } + } + + if (converted_signature) { + WebKitDOMNode *node; + + while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (converted_signature)))) + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (insert_signature_in), node, NULL); + remove_node (WEBKIT_DOM_NODE (converted_signature)); + } else + webkit_dom_html_element_insert_adjacent_html ( + WEBKIT_DOM_HTML_ELEMENT (insert_signature_in), + "beforeend", + signature_text, + NULL); + + element = webkit_dom_element_query_selector ( + insert_signature_in, "[data-evo-signature-plain-text-mode]", NULL); + if (element) + webkit_dom_element_remove_attribute ( + element, "data-evo-signature-plain-text-mode"); + g_free (signature_text); + +insert: + /* Remove the old signature and insert the new one. */ + signatures = webkit_dom_document_get_elements_by_class_name_as_html_collection ( + document, "-x-evo-signature-wrapper"); + list_length = webkit_dom_html_collection_get_length (signatures); + for (ii = 0; ii < list_length; ii++) { + WebKitDOMNode *wrapper, *signature; + + wrapper = webkit_dom_html_collection_item (signatures, ii); + signature = webkit_dom_node_get_first_child (wrapper); + + /* Old messages will have the signature id in the name attribute, correct it. */ + element_rename_attribute (WEBKIT_DOM_ELEMENT (signature), "name", "id"); + + /* When we are editing a message with signature, we need to unset the + * active signature id as if the signature in the message was edited + * by the user we would discard these changes. */ + if (*set_signature_from_message && content) { + if (*check_if_signature_is_changed) { + /* Normalize the signature that we want to insert as the one in the + * message already is normalized. */ + webkit_dom_node_normalize (WEBKIT_DOM_NODE (signature_to_insert)); + if (!webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (signature_to_insert), signature)) { + /* Signature in the body is different than the one with the + * same id, so set the active signature to None and leave + * the signature that is in the body. */ + new_signature_id = g_strdup ("none"); + *ignore_next_signature_change = TRUE; + } + + *check_if_signature_is_changed = FALSE; + *set_signature_from_message = FALSE; + } else { + /* Load the signature and check if is it the same + * as the signature in body or the user previously + * changed it. */ + new_signature_id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (signature)); + *check_if_signature_is_changed = TRUE; + } + g_object_unref (wrapper); + g_clear_object (&signatures); + + return new_signature_id; + } + + /* If the top signature was set we have to remove the newline + * that was inserted after it */ + if (top_signature) { + WebKitDOMElement *spacer; + + spacer = webkit_dom_document_query_selector ( + document, ".-x-evo-top-signature-spacer", NULL); + if (spacer) + remove_node_if_empty (WEBKIT_DOM_NODE (spacer)); + } + + /* Leave just one signature wrapper there as it will be reused. */ + if (ii != list_length - 1) { + g_object_unref (wrapper); + remove_node (wrapper); + } else { + remove_node (signature); + signature_wrapper = WEBKIT_DOM_ELEMENT (wrapper); + } + } + + if (signature_wrapper) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (signature_wrapper), + WEBKIT_DOM_NODE (signature_to_insert), + NULL); + + /* Insert a spacer below the top signature */ + if (top_signature && content) { + WebKitDOMElement *spacer; + + spacer = prepare_top_signature_spacer (editor_page); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (spacer), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (signature_wrapper)), + NULL); + } + + g_object_unref (signature_wrapper); + } else { + signature_wrapper = webkit_dom_document_create_element (document, "div", NULL); + webkit_dom_element_set_class_name (signature_wrapper, "-x-evo-signature-wrapper"); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (signature_wrapper), + WEBKIT_DOM_NODE (signature_to_insert), + NULL); + + if (top_signature) { + WebKitDOMNode *child; + + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + if (start_typing_at_bottom ()) { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (signature_wrapper), + child, + NULL); + } else { + /* When we are using signature on top the caret + * should be before the signature */ + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (signature_wrapper), + child, + NULL); + } + } else { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (signature_wrapper), + NULL); + } + + move_caret_after_signature_inserted (editor_page); + } + g_clear_object (&signatures); + + if (is_html && html_mode) + e_editor_dom_fix_file_uri_images (editor_page); + + /* Make sure the flag will be unset and won't influence user's choice */ + *set_signature_from_message = FALSE; + + return NULL; +} + +gchar * +e_composer_dom_get_active_signature_uid (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + gchar *uid = NULL; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + + if ((element = webkit_dom_document_query_selector (document, ".-x-evo-signature[id]", NULL))) + uid = webkit_dom_element_get_id (element); + + return uid; +} + +gchar * +e_composer_dom_get_raw_body_content_without_signature (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *list = NULL; + GString* content; + gulong ii, length; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + + content = g_string_new (NULL); + + list = webkit_dom_document_query_selector_all ( + document, "body > *:not(.-x-evo-signature-wrapper)", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) { + gchar *text; + + text = webkit_dom_html_element_get_inner_text (WEBKIT_DOM_HTML_ELEMENT (node)); + g_string_append (content, text); + g_free (text); + + if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (node)) + g_string_append (content, "\n"); + else + g_string_append (content, " "); + } + } + g_clear_object (&list); + + return g_string_free (content, FALSE); +} + +gchar * +e_composer_dom_get_raw_body_content (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + + body = webkit_dom_document_get_body (document); + + return webkit_dom_html_element_get_inner_text (body); +} + +static void +insert_nbsp_history_event (WebKitDOMDocument *document, + EEditorUndoRedoManager *manager, + gboolean delete, + guint x, + guint y) +{ + EEditorHistoryEvent *event; + WebKitDOMDocumentFragment *fragment; + + event = g_new0 (EEditorHistoryEvent, 1); + event->type = HISTORY_AND; + e_editor_undo_redo_manager_insert_history_event (manager, event); + + fragment = webkit_dom_document_create_document_fragment (document); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + webkit_dom_document_create_text_node (document, UNICODE_NBSP)), + NULL); + + event = g_new0 (EEditorHistoryEvent, 1); + event->type = HISTORY_DELETE; + + if (delete) + g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (1)); + + event->data.fragment = fragment; + + event->before.start.x = x; + event->before.start.y = y; + event->before.end.x = x; + event->before.end.y = y; + + event->after.start.x = x; + event->after.start.y = y; + event->after.end.x = x; + event->after.end.y = y; + + e_editor_undo_redo_manager_insert_history_event (manager, event); +} + +void +e_composer_dom_save_drag_and_drop_history (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMRange *beginning_of_line = NULL; + WebKitDOMRange *range = NULL, *range_clone = NULL; + EEditorHistoryEvent *event; + EEditorUndoRedoManager *manager; + gboolean start_to_start, end_to_end; + gchar *range_text; + guint x, y; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (!(dom_window = webkit_dom_document_get_default_view (document))) + return; + + if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) { + g_clear_object (&dom_window); + return; + } + + g_clear_object (&dom_window); + + if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { + g_clear_object (&dom_selection); + return; + } + + /* Obtain the dragged content. */ + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + range_clone = webkit_dom_range_clone_range (range, NULL); + + /* Create the history event for the content that will + * be removed by DnD. */ + event = g_new0 (EEditorHistoryEvent, 1); + event->type = HISTORY_DELETE; + + e_editor_dom_selection_get_coordinates (editor_page, + &event->before.start.x, + &event->before.start.y, + &event->before.end.x, + &event->before.end.y); + + x = event->before.start.x; + y = event->before.start.y; + + event->after.start.x = x; + event->after.start.y = y; + event->after.end.x = x; + event->after.end.y = y; + + /* Save the content that will be removed. */ + fragment = webkit_dom_range_clone_contents (range_clone, NULL); + + /* Extend the cloned range to point one character after + * the selection ends to later check if there is a whitespace + * after it. */ + webkit_dom_range_set_end ( + range_clone, + webkit_dom_range_get_end_container (range_clone, NULL), + webkit_dom_range_get_end_offset (range_clone, NULL) + 1, + NULL); + range_text = webkit_dom_range_get_text (range_clone); + + /* Check if the current selection starts on the beginning + * of line. */ + webkit_dom_dom_selection_modify ( + dom_selection, "extend", "left", "lineboundary"); + beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + start_to_start = webkit_dom_range_compare_boundary_points ( + beginning_of_line, 0 /* START_TO_START */, range, NULL) == 0; + + /* Restore the selection to state before the check. */ + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&beginning_of_line); + + /* Check if the current selection end on the end of the line. */ + webkit_dom_dom_selection_modify ( + dom_selection, "extend", "right", "lineboundary"); + beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + end_to_end = webkit_dom_range_compare_boundary_points ( + beginning_of_line, 2 /* END_TO_END */, range, NULL) == 0; + + /* Dragging the whole line. */ + if (start_to_start && end_to_end) { + WebKitDOMNode *container, *actual_block, *tmp_block; + + /* Select the whole line (to the beginning of the next + * one so we can reuse the undo code while undoing this. + * Because of this we need to special mark the event + * with history-drag-and-drop to correct the selection + * after undoing it (otherwise the beginning of the next + * line will be selected as well. */ + webkit_dom_dom_selection_modify ( + dom_selection, "extend", "right", "character"); + g_clear_object (&beginning_of_line); + beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + container = webkit_dom_range_get_end_container (range, NULL); + actual_block = e_editor_dom_get_parent_block_node_from_child (container); + + tmp_block = webkit_dom_range_get_end_container (beginning_of_line, NULL); + if ((tmp_block = e_editor_dom_get_parent_block_node_from_child (tmp_block))) { + e_editor_dom_selection_get_coordinates (editor_page, + &event->before.start.x, + &event->before.start.y, + &event->before.end.x, + &event->before.end.y); + + /* Create the right content for the history event. */ + fragment = webkit_dom_document_create_document_fragment (document); + /* The removed line. */ + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (actual_block, TRUE, NULL), + NULL); + /* The following block, but empty. */ + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (tmp_block, FALSE, NULL), + NULL); + g_object_set_data ( + G_OBJECT (fragment), + "history-drag-and-drop", + GINT_TO_POINTER (1)); + /* It should act as a Delete key press. */ + g_object_set_data ( + G_OBJECT (fragment), + "history-delete-key", + GINT_TO_POINTER (1)); + } + } + + event->data.fragment = fragment; + e_editor_undo_redo_manager_insert_history_event (manager, event); + + /* Selection is ending on the end of the line, check if + * there is a space before the selection start. If so, it + * will be removed and we need create the history event + * for it. */ + if (end_to_end) { + gchar *range_text_start; + glong start_offset; + + start_offset = webkit_dom_range_get_start_offset (range_clone, NULL); + webkit_dom_range_set_start ( + range_clone, + webkit_dom_range_get_start_container (range_clone, NULL), + start_offset > 0 ? start_offset - 1 : 0, + NULL); + + range_text_start = webkit_dom_range_get_text (range_clone); + if (g_str_has_prefix (range_text_start, " ") || + g_str_has_prefix (range_text_start, UNICODE_NBSP)) + insert_nbsp_history_event (document, manager, FALSE, x, y); + + g_free (range_text_start); + } + + /* WebKit removes the space (if presented) after selection and + * we need to create a new history event for it. */ + if (g_str_has_suffix (range_text, " ") || + g_str_has_suffix (range_text, UNICODE_NBSP)) + insert_nbsp_history_event (document, manager, TRUE, x, y); + + g_free (range_text); + + /* Restore the selection to original state. */ + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&beginning_of_line); + + /* All the things above were about removing the content, + * create an AND event to continue later with inserting + * the dropped content. */ + event = g_new0 (EEditorHistoryEvent, 1); + event->type = HISTORY_AND; + e_editor_undo_redo_manager_insert_history_event (manager, event); + + g_clear_object (&dom_selection); + + g_clear_object (&range); + g_clear_object (&range_clone); +} + +void +e_composer_dom_clean_after_drag_and_drop (EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + e_editor_dom_save_history_for_drop (editor_page); + e_editor_dom_check_magic_links (editor_page, FALSE); +} diff --git a/modules/webkit-editor/web-extension/e-composer-dom-functions.h b/modules/webkit-editor/web-extension/e-composer-dom-functions.h new file mode 100644 index 0000000..8e0f934 --- /dev/null +++ b/modules/webkit-editor/web-extension/e-composer-dom-functions.h @@ -0,0 +1,48 @@ +/* + * e-composer-dom-functions.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_COMPOSER_DOM_FUNCTIONS_H +#define E_COMPOSER_DOM_FUNCTIONS_H + +#include <webkitdom/webkitdom.h> + +#include "e-editor-page.h" + +G_BEGIN_DECLS + +gchar * e_composer_dom_insert_signature (EEditorPage *editor_page, + const gchar *content, + gboolean is_html, + const gchar *id, + gboolean *set_signature_from_message, + gboolean *check_if_signature_is_changed, + gboolean *ignore_next_signature_change); +gchar * e_composer_dom_get_active_signature_uid + (EEditorPage *editor_page); +gchar * e_composer_dom_get_raw_body_content_without_signature + (EEditorPage *editor_page); +gchar * e_composer_dom_get_raw_body_content + (EEditorPage *editor_page); +void e_composer_dom_save_drag_and_drop_history + (EEditorPage *editor_page); +void e_composer_dom_clean_after_drag_and_drop + (EEditorPage *editor_page); + +G_END_DECLS + +#endif /* E_COMPOSER_DOM_FUNCTIONS_H */ diff --git a/modules/webkit-editor/web-extension/e-dialogs-dom-functions.c b/modules/webkit-editor/web-extension/e-dialogs-dom-functions.c new file mode 100644 index 0000000..84d17ff --- /dev/null +++ b/modules/webkit-editor/web-extension/e-dialogs-dom-functions.c @@ -0,0 +1,1455 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define WEBKIT_DOM_USE_UNSTABLE_API +#include <webkitdom/WebKitDOMDOMSelection.h> +#include <webkitdom/WebKitDOMDOMWindowUnstable.h> +#undef WEBKIT_DOM_USE_UNSTABLE_API + +#include "web-extensions/e-dom-utils.h" + +#include "e-editor-dom-functions.h" +#include "e-editor-undo-redo-manager.h" + +#include "e-dialogs-dom-functions.h" + +/* ******************** Cell Dialog ***************** */ + +typedef void (*DOMStrFunc) (WebKitDOMHTMLTableCellElement *cell, const gchar *val, gpointer user_data); +typedef void (*DOMLongFunc) (WebKitDOMHTMLTableCellElement *cell, glong val, gpointer user_data); +typedef void (*DOMBoolFunc) (WebKitDOMHTMLTableCellElement *cell, gboolean val, gpointer user_data); + +static WebKitDOMElement * +get_current_cell_element (WebKitDOMDocument *document) +{ + return webkit_dom_document_get_element_by_id (document, "-x-evo-current-cell"); +} + +static void +call_cell_dom_func (WebKitDOMHTMLTableCellElement *cell, + gpointer func, + GValue *value, + gpointer user_data) +{ + if (G_VALUE_HOLDS_STRING (value)) { + DOMStrFunc f = func; + f (cell, g_value_get_string (value), user_data); + } else if (G_VALUE_HOLDS_LONG (value)) { + DOMLongFunc f = func; + f (cell, g_value_get_long (value), user_data); + } else if (G_VALUE_HOLDS_BOOLEAN (value)) { + DOMBoolFunc f = func; + f (cell, g_value_get_boolean (value), user_data); + } +} + +static void +for_each_cell_do (WebKitDOMElement *row, + gpointer func, + GValue *value, + gpointer user_data) +{ + WebKitDOMHTMLCollection *cells = NULL; + gulong ii, length; + + cells = webkit_dom_html_table_row_element_get_cells ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + length = webkit_dom_html_collection_get_length (cells); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *cell; + cell = webkit_dom_html_collection_item (cells, ii); + if (!cell) { + continue; + } + + call_cell_dom_func ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), func, value, user_data); + g_object_unref (cell); + } + g_clear_object (&cells); +} + +static void +cell_dialog_set_attribute (WebKitDOMDocument *document, + EContentEditorScope scope, + gpointer func, + GValue *value, + gpointer user_data) +{ + WebKitDOMElement *cell = get_current_cell_element (document); + + if (scope == E_CONTENT_EDITOR_SCOPE_CELL) { + + call_cell_dom_func ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), + func, value, user_data); + + } else if (scope == E_CONTENT_EDITOR_SCOPE_COLUMN) { + gulong index, ii, length; + WebKitDOMElement *table; + WebKitDOMHTMLCollection *rows = NULL; + + index = webkit_dom_html_table_cell_element_get_cell_index ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell)); + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE"); + if (!table) { + return; + } + + rows = webkit_dom_html_table_element_get_rows ( + WEBKIT_DOM_HTML_TABLE_ELEMENT (table)); + length = webkit_dom_html_collection_get_length (rows); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *row, *cell; + WebKitDOMHTMLCollection *cells = NULL; + + row = webkit_dom_html_collection_item (rows, ii); + cells = webkit_dom_html_table_row_element_get_cells ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + cell = webkit_dom_html_collection_item (cells, index); + if (!cell) { + g_object_unref (row); + g_clear_object (&cells); + continue; + } + + call_cell_dom_func ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), + func, value, user_data); + g_object_unref (row); + g_clear_object (&cells); + g_object_unref (cell); + } + g_clear_object (&rows); + + } else if (scope == E_CONTENT_EDITOR_SCOPE_ROW) { + WebKitDOMElement *row; + + row = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR"); + if (!row) { + return; + } + + for_each_cell_do (row, func, value, user_data); + + } else if (scope == E_CONTENT_EDITOR_SCOPE_TABLE) { + gulong ii, length; + WebKitDOMElement *table; + WebKitDOMHTMLCollection *rows = NULL; + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE"); + if (!table) { + return; + } + + rows = webkit_dom_html_table_element_get_rows ( + WEBKIT_DOM_HTML_TABLE_ELEMENT (table)); + length = webkit_dom_html_collection_get_length (rows); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *row; + + row = webkit_dom_html_collection_item (rows, ii); + if (!row) { + g_object_unref (row); + continue; + } + + for_each_cell_do ( + WEBKIT_DOM_ELEMENT (row), func, value, user_data); + g_object_unref (row); + } + g_clear_object (&rows); + } +} + +static void +cell_set_header_style (WebKitDOMHTMLTableCellElement *cell, + gboolean header_style, + gpointer user_data) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *nodes = NULL; + WebKitDOMElement *new_cell; + gulong length, ii; + gchar *tagname; + + document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (cell)); + tagname = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (cell)); + + if (header_style && (g_ascii_strncasecmp (tagname, "TD", 2) == 0)) { + + new_cell = webkit_dom_document_create_element (document, "TH", NULL); + + } else if (!header_style && (g_ascii_strncasecmp (tagname, "TH", 2) == 0)) { + + new_cell = webkit_dom_document_create_element (document, "TD", NULL); + + } else { + g_free (tagname); + return; + } + + webkit_dom_element_set_id (new_cell, "-x-evo-current-cell"); + + /* Move all child nodes from cell to new_cell */ + nodes = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (cell)); + length = webkit_dom_node_list_get_length (nodes); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (nodes, ii); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (new_cell), node, NULL); + g_object_unref (node); + } + g_clear_object (&nodes); + + /* Insert new_cell before cell and remove cell */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (cell)), + WEBKIT_DOM_NODE (new_cell), + WEBKIT_DOM_NODE (cell), NULL); + + webkit_dom_node_remove_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (cell)), + WEBKIT_DOM_NODE (cell), NULL); + + g_free (tagname); +} + +void +e_dialogs_dom_cell_mark_current_cell_element (EEditorPage *editor_page, + const gchar *id) +{ + EEditorUndoRedoManager *manager; + WebKitDOMElement *cell; + WebKitDOMDocument *document; + WebKitDOMNode *node_under_mouse_click; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + g_return_if_fail (id != NULL); + + document = e_editor_page_get_document (editor_page); + + node_under_mouse_click = e_editor_page_get_node_under_mouse_click (editor_page); + + if (node_under_mouse_click && WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node_under_mouse_click)) { + cell = WEBKIT_DOM_ELEMENT (node_under_mouse_click); + } else { + WebKitDOMElement *selection_start; + + e_editor_dom_selection_save (editor_page); + + selection_start = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (selection_start), "TD"); + if (!cell) + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (selection_start), "TH"); + + e_editor_dom_selection_restore (editor_page); + } + + if (cell) + webkit_dom_element_set_id (cell, "-x-evo-current-cell"); + else + return; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + EEditorHistoryEvent *ev; + WebKitDOMElement *table; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_TABLE_DIALOG; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + table = dom_node_find_parent_element ( + WEBKIT_DOM_NODE (cell), "TABLE"); + if (table) + ev->data.dom.from = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (table), TRUE, NULL); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } +} + +void +e_dialogs_dom_cell_save_history_on_exit (EEditorPage *editor_page) +{ + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + WebKitDOMElement *cell, *table; + WebKitDOMDocument *document; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + cell = get_current_cell_element (document); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE"); + g_return_if_fail (table != NULL); + + webkit_dom_element_remove_attribute (cell, "id"); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + ev->data.dom.to = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (table), TRUE, NULL); + + if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) + e_editor_undo_redo_manager_remove_current_history_event (manager); + else + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); +} + +void +e_dialogs_dom_cell_set_element_v_align (EEditorPage *editor_page, + const gchar *v_align, + EContentEditorScope scope) +{ + GValue val = { 0 }; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_value_init (&val, G_TYPE_STRING); + g_value_set_string (&val, v_align); + + cell_dialog_set_attribute (e_editor_page_get_document (editor_page), + scope, webkit_dom_html_table_cell_element_set_v_align, &val, NULL); + + g_value_unset (&val); +} + +void +e_dialogs_dom_cell_set_element_align (EEditorPage *editor_page, + const gchar *align, + EContentEditorScope scope) +{ + GValue val = { 0 }; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_value_init (&val, G_TYPE_STRING); + g_value_set_string (&val, align); + + cell_dialog_set_attribute (e_editor_page_get_document (editor_page), + scope, webkit_dom_html_table_cell_element_set_align, &val, NULL); + + g_value_unset (&val); +} + +void +e_dialogs_dom_cell_set_element_no_wrap (EEditorPage *editor_page, + gboolean wrap_text, + EContentEditorScope scope) +{ + GValue val = { 0 }; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_value_init (&val, G_TYPE_BOOLEAN); + g_value_set_boolean (&val, wrap_text); + + cell_dialog_set_attribute (e_editor_page_get_document (editor_page), + scope, webkit_dom_html_table_cell_element_set_no_wrap, &val, NULL); +} + +void +e_dialogs_dom_cell_set_element_header_style (EEditorPage *editor_page, + gboolean header_style, + EContentEditorScope scope) +{ + GValue val = { 0 }; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_value_init (&val, G_TYPE_BOOLEAN); + g_value_set_boolean (&val, header_style); + + cell_dialog_set_attribute (e_editor_page_get_document (editor_page), + scope, cell_set_header_style, &val, NULL); +} + +void +e_dialogs_dom_cell_set_element_width (EEditorPage *editor_page, + const gchar *width, + EContentEditorScope scope) +{ + GValue val = { 0 }; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_value_init (&val, G_TYPE_STRING); + g_value_set_string (&val, width); + + cell_dialog_set_attribute (e_editor_page_get_document (editor_page), + scope, webkit_dom_html_table_cell_element_set_width, &val, NULL); + + g_value_unset (&val); +} + +void +e_dialogs_dom_cell_set_element_col_span (EEditorPage *editor_page, + glong span, + EContentEditorScope scope) +{ + GValue val = { 0 }; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_value_init (&val, G_TYPE_LONG); + g_value_set_long (&val, span); + + cell_dialog_set_attribute (e_editor_page_get_document (editor_page), + scope, webkit_dom_html_table_cell_element_set_col_span, &val, NULL); +} + +void +e_dialogs_dom_cell_set_element_row_span (EEditorPage *editor_page, + glong span, + EContentEditorScope scope) +{ + GValue val = { 0 }; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_value_init (&val, G_TYPE_LONG); + g_value_set_long (&val, span); + + cell_dialog_set_attribute (e_editor_page_get_document (editor_page), + scope, webkit_dom_html_table_cell_element_set_row_span, &val, NULL); +} + +void +e_dialogs_dom_cell_set_element_bg_color (EEditorPage *editor_page, + const gchar *color, + EContentEditorScope scope) +{ + GValue val = { 0 }; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_value_init (&val, G_TYPE_STRING); + g_value_set_string (&val, color); + + cell_dialog_set_attribute (e_editor_page_get_document (editor_page), + scope, webkit_dom_html_table_cell_element_set_bg_color, &val, NULL); +} + +/* ******************** HRule Dialog ***************** */ + +static WebKitDOMElement * +get_current_hrule_element (WebKitDOMDocument *document) +{ + return webkit_dom_document_get_element_by_id (document, "-x-evo-current-hr"); +} + +gboolean +e_dialogs_dom_h_rule_find_hrule (EEditorPage *editor_page) +{ + EEditorUndoRedoManager *manager; + gboolean created = FALSE; + WebKitDOMDocument *document; + WebKitDOMElement *rule; + WebKitDOMNode *node_under_mouse_click; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + + node_under_mouse_click = e_editor_page_get_node_under_mouse_click (editor_page); + + if (node_under_mouse_click && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node_under_mouse_click)) { + rule = WEBKIT_DOM_ELEMENT (node_under_mouse_click); + webkit_dom_element_set_id (rule, "-x-evo-current-hr"); + } else { + WebKitDOMElement *selection_start, *parent; + + e_editor_dom_selection_save (editor_page); + + selection_start = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start)); + + rule = webkit_dom_document_create_element (document, "HR", NULL); + webkit_dom_element_set_id (rule, "-x-evo-current-hr"); + + /* Insert horizontal rule into body below the caret */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)), + WEBKIT_DOM_NODE (rule), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)), + NULL); + + e_editor_dom_selection_restore (editor_page); + + e_editor_page_emit_content_changed (editor_page); + + created = TRUE; + } + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + EEditorHistoryEvent *ev; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_HRULE_DIALOG; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + if (!created) + ev->data.dom.from = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (rule), FALSE, NULL); + else + ev->data.dom.from = NULL; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + return created; +} + +void +e_dialogs_dom_h_rule_dialog_on_close (EEditorPage *editor_page) +{ + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + WebKitDOMDocument *document; + WebKitDOMElement *element; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + element = get_current_hrule_element (document); + g_return_if_fail (element != NULL); + + webkit_dom_element_remove_attribute (element, "id"); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + + ev->data.dom.to = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (element), TRUE, NULL); + + if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) + e_editor_undo_redo_manager_remove_current_history_event (manager); + else + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); +} + +/* ******************** Image Dialog ***************** */ + +static WebKitDOMElement * +get_current_image_element (WebKitDOMDocument *document) +{ + return webkit_dom_document_get_element_by_id (document, "-x-evo-current-img"); +} + +void +e_dialogs_dom_image_mark_image (EEditorPage *editor_page) +{ + EEditorUndoRedoManager *manager; + WebKitDOMNode *node_under_mouse_click; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + node_under_mouse_click = e_editor_page_get_node_under_mouse_click (editor_page); + + g_return_if_fail (node_under_mouse_click && WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node_under_mouse_click)); + + webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (node_under_mouse_click), "-x-evo-current-img"); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + EEditorHistoryEvent *ev; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_IMAGE_DIALOG; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + ev->data.dom.from = webkit_dom_node_clone_node_with_error (node_under_mouse_click, FALSE, NULL); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } +} + +void +e_dialogs_dom_image_save_history_on_exit (EEditorPage *editor_page) +{ + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + WebKitDOMDocument *document; + WebKitDOMElement *element; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + element = get_current_image_element (document); + g_return_if_fail (element != NULL); + + webkit_dom_element_remove_attribute (element, "id"); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + ev->data.dom.to = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (element), TRUE, NULL); + + if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) + e_editor_undo_redo_manager_remove_current_history_event (manager); + else + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); +} + +void +e_dialogs_dom_image_set_element_url (EEditorPage *editor_page, + const gchar *url) +{ + WebKitDOMElement *image, *link; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + image = get_current_image_element (e_editor_page_get_document (editor_page)); + link = dom_node_find_parent_element (WEBKIT_DOM_NODE (image), "A"); + + if (link) { + if (!url || !*url) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (link)), + WEBKIT_DOM_NODE (image), + WEBKIT_DOM_NODE (link), NULL); + webkit_dom_node_remove_child ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (link)), + WEBKIT_DOM_NODE (link), NULL); + } else { + webkit_dom_html_anchor_element_set_href ( + WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url); + } + } else { + if (url && *url) { + WebKitDOMDocument *document; + + document = webkit_dom_node_get_owner_document ( + WEBKIT_DOM_NODE (image)); + link = webkit_dom_document_create_element ( + document, "A", NULL); + + webkit_dom_html_anchor_element_set_href ( + WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (image)), + WEBKIT_DOM_NODE (link), + WEBKIT_DOM_NODE (image), NULL); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (link), + WEBKIT_DOM_NODE (image), NULL); + } + } +} + +gchar * +e_dialogs_dom_image_get_element_url (EEditorPage *editor_page) +{ + gchar *value = NULL; + WebKitDOMElement *image, *link; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + image = get_current_image_element (e_editor_page_get_document (editor_page)); + link = dom_node_find_parent_element (WEBKIT_DOM_NODE (image), "A"); + + if (link) + value = webkit_dom_element_get_attribute (link, "href"); + + return value; +} + +/* ******************** Link Dialog ***************** */ + +void +e_dialogs_dom_link_commit (EEditorPage *editor_page, + const gchar *url, + const gchar *inner_text) +{ + WebKitDOMDocument *document; + WebKitDOMElement *link; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + link = webkit_dom_document_get_element_by_id (document, "-x-evo-current-anchor"); + + if (link) { + WebKitDOMElement *element; + + webkit_dom_html_anchor_element_set_href ( + WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (link), inner_text, NULL); + + element = webkit_dom_document_create_element (document, "SPAN", NULL); + webkit_dom_element_set_id (element, "-x-evo-selection-end-marker"); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (link)), + WEBKIT_DOM_NODE (element), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (link)), + NULL); + + element = webkit_dom_document_create_element (document, "SPAN", NULL); + webkit_dom_element_set_id (element, "-x-evo-selection-start-marker"); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (link)), + WEBKIT_DOM_NODE (element), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (link)), + NULL); + + e_editor_dom_selection_restore (editor_page); + } else { + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + e_editor_dom_selection_restore (editor_page); + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + if (webkit_dom_range_get_collapsed (range, NULL)) { + WebKitDOMElement *selection_marker; + WebKitDOMElement *anchor; + + e_editor_dom_selection_save (editor_page); + selection_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + anchor = webkit_dom_document_create_element (document, "A", NULL); + webkit_dom_element_set_attribute (anchor, "href", url, NULL); + webkit_dom_element_set_id (anchor, "-x-evo-current-anchor"); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (anchor), inner_text, NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_marker)), + WEBKIT_DOM_NODE (anchor), + WEBKIT_DOM_NODE (selection_marker), + NULL); + e_editor_dom_selection_restore (editor_page); + } else { + gchar *text; + + text = webkit_dom_range_get_text (range); + if (text && *text) { + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev; + + e_editor_dom_create_link (editor_page, url); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + + ev->data.dom.from = + WEBKIT_DOM_NODE (webkit_dom_document_create_text_node (document, text)); + + webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); + } + g_free (text); + } + + g_clear_object (&range); + g_clear_object (&dom_selection); + } +} + +void +e_dialogs_dom_link_dialog_on_close (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *link; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + link = webkit_dom_document_get_element_by_id (document, "-x-evo-current-anchor"); + if (link) { + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + if (ev->type == HISTORY_LINK_DIALOG) { + ev->data.dom.to = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (link), TRUE, NULL); + + if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) + e_editor_undo_redo_manager_remove_current_history_event (manager); + else + e_editor_dom_selection_get_coordinates ( + editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); + } + webkit_dom_element_remove_attribute (link, "id"); + } +} + +void +e_dialogs_dom_link_dialog_on_open (EEditorPage *editor_page) +{ + EEditorUndoRedoManager *manager; + WebKitDOMDocument *document; + WebKitDOMElement *link = NULL; + WebKitDOMNode *node_under_mouse_click; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + node_under_mouse_click = e_editor_page_get_node_under_mouse_click (editor_page); + if (node_under_mouse_click && WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node_under_mouse_click)) { + link = WEBKIT_DOM_ELEMENT (node_under_mouse_click); + } else { + if (!(link = webkit_dom_document_get_element_by_id (document, "-x-evo-current-anchor"))) { + if (node_under_mouse_click) { + link = dom_node_find_parent_element (node_under_mouse_click, "A"); + } else { + WebKitDOMElement *selection_start; + + e_editor_dom_selection_save (editor_page); + + selection_start = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + link = dom_node_find_parent_element (WEBKIT_DOM_NODE (selection_start), "A"); + + e_editor_dom_selection_restore (editor_page); + } + } + } + + if (link) + webkit_dom_element_set_id (link, "-x-evo-current-anchor"); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + EEditorHistoryEvent *ev; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_LINK_DIALOG; + + e_editor_dom_selection_get_coordinates ( + editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + if (link) + ev->data.dom.from = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (link), TRUE, NULL); + else + ev->data.dom.from = NULL; + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } +} + +GVariant * +e_dialogs_dom_link_show (EEditorPage *editor_page) +{ + GVariant *result = NULL; + WebKitDOMDocument *document; + WebKitDOMElement *link; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + + e_editor_dom_selection_save (editor_page); + + link = webkit_dom_document_get_element_by_id (document, "-x-evo-current-anchor"); + if (link) { + gchar *href, *text; + + href = webkit_dom_element_get_attribute (link, "href"); + text = webkit_dom_html_element_get_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (link)); + + result = g_variant_new ("(ss)", href, text); + + g_free (text); + g_free (href); + } else { + gchar *text; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + /* No selection at all */ + if (!dom_selection || webkit_dom_dom_selection_get_range_count (dom_selection) < 1) + result = g_variant_new ("(ss)", "", ""); + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + text = webkit_dom_range_get_text (range); + if (text) + result = g_variant_new ("(ss)", "", text); + + g_free (text); + + g_clear_object (&range); + g_clear_object (&dom_selection); + } + + return result; +} + +/* ******************** Page Dialog ***************** */ + +void +e_dialogs_dom_page_save_history (EEditorPage *editor_page) +{ + EEditorUndoRedoManager *manager; + WebKitDOMDocument *document; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + EEditorHistoryEvent *ev; + WebKitDOMHTMLElement *body; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_PAGE_DIALOG; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + body = webkit_dom_document_get_body (document); + ev->data.dom.from = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), FALSE, NULL); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } +} + +void +e_dialogs_dom_page_save_history_on_exit (EEditorPage *editor_page) +{ + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + body = webkit_dom_document_get_body (document); + ev->data.dom.to = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), FALSE, NULL); + + if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) { + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); + } else { + e_editor_undo_redo_manager_remove_current_history_event (manager); + } +} + +/* ******************** Spell Check Dialog ***************** */ + +static gboolean +select_next_word (WebKitDOMDOMSelection *dom_selection) +{ + gulong anchor_offset, focus_offset; + WebKitDOMNode *anchor, *focus; + + anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection); + anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection); + + focus = webkit_dom_dom_selection_get_focus_node (dom_selection); + focus_offset = webkit_dom_dom_selection_get_focus_offset (dom_selection); + + /* Jump _behind_ next word */ + webkit_dom_dom_selection_modify (dom_selection, "move", "forward", "word"); + /* Jump before the word */ + webkit_dom_dom_selection_modify (dom_selection, "move", "backward", "word"); + /* Select it */ + webkit_dom_dom_selection_modify (dom_selection, "extend", "forward", "word"); + + /* If the selection didn't change, then we have most probably + * reached the end of document - return FALSE */ + return !((anchor == webkit_dom_dom_selection_get_anchor_node (dom_selection)) && + (anchor_offset == webkit_dom_dom_selection_get_anchor_offset (dom_selection)) && + (focus == webkit_dom_dom_selection_get_focus_node (dom_selection)) && + (focus_offset == webkit_dom_dom_selection_get_focus_offset (dom_selection))); +} + +static gboolean +select_previous_word (WebKitDOMDOMSelection *dom_selection) +{ + WebKitDOMNode *old_anchor_node; + WebKitDOMNode *new_anchor_node; + gulong old_anchor_offset; + gulong new_anchor_offset; + + old_anchor_node = webkit_dom_dom_selection_get_anchor_node (dom_selection); + old_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection); + + /* Jump on the beginning of current word */ + webkit_dom_dom_selection_modify (dom_selection, "move", "backward", "word"); + /* Jump before previous word */ + webkit_dom_dom_selection_modify (dom_selection, "move", "backward", "word"); + /* Select it */ + webkit_dom_dom_selection_modify (dom_selection, "extend", "forward", "word"); + + /* If the selection start didn't change, then we have most probably + * reached the beginnig of document. Return FALSE */ + + new_anchor_node = webkit_dom_dom_selection_get_anchor_node (dom_selection); + new_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection); + + return (new_anchor_node != old_anchor_node) || + (new_anchor_offset != old_anchor_offset); +} + +static gchar * +e_dialogs_dom_spell_check_run (EEditorPage *editor_page, + gboolean run_next, + const gchar *from_word, + const gchar * const *languages) +{ + gulong start_offset = 0, end_offset = 0; + WebKitDOMDocument *document; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMNode *start = NULL, *end = NULL; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (!from_word || !*from_word) { + if (run_next) { + webkit_dom_dom_selection_modify ( + dom_selection, "move", "left", "documentboundary"); + } else { + webkit_dom_dom_selection_modify ( + dom_selection, "move", "right", "documentboundary"); + webkit_dom_dom_selection_modify ( + dom_selection, "extend", "backward", "word"); + } + } else { + /* Remember last selected word */ + start = webkit_dom_dom_selection_get_anchor_node (dom_selection); + end = webkit_dom_dom_selection_get_focus_node (dom_selection); + start_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection); + end_offset = webkit_dom_dom_selection_get_focus_offset (dom_selection); + } + + while ((run_next ? select_next_word (dom_selection) : select_previous_word (dom_selection))) { + WebKitDOMRange *range = NULL; + gchar *word; + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + word = webkit_dom_range_get_text (range); + g_clear_object (&range); + + if (!e_editor_page_check_word_spelling (editor_page, word, languages)) { + /* Found misspelled word! */ + return word; + } + + g_free (word); + } + + /* Restore the selection to contain the last misspelled word. This is + * reached only when we reach the beginning/end of the document */ + if (start && end) + webkit_dom_dom_selection_set_base_and_extent ( + dom_selection, start, start_offset, end, end_offset, NULL); + + g_clear_object (&dom_selection); + + return NULL; +} + +gchar * +e_dialogs_dom_spell_check_next (EEditorPage *editor_page, + const gchar *from_word, + const gchar * const *languages) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return e_dialogs_dom_spell_check_run (editor_page, TRUE, from_word, languages); +} + +gchar * +e_dialogs_dom_spell_check_prev (EEditorPage *editor_page, + const gchar *from_word, + const gchar * const *languages) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return e_dialogs_dom_spell_check_run (editor_page, FALSE, from_word, languages); +} + +/* ******************** Table Dialog ***************** */ + +static WebKitDOMHTMLTableElement * +get_current_table_element (WebKitDOMDocument *document) +{ + return WEBKIT_DOM_HTML_TABLE_ELEMENT (webkit_dom_document_get_element_by_id (document, "-x-evo-current-table")); +} + +void +e_dialogs_dom_table_set_row_count (EEditorPage *editor_page, + gulong expected_count) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLCollection *rows = NULL, *cells = NULL; + WebKitDOMHTMLTableElement *table_element; + WebKitDOMHTMLTableRowElement *row; + gulong ii, rows_current_count, cells_current_count; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_element = get_current_table_element (document); + if (!table_element) + return; + + rows = webkit_dom_html_table_element_get_rows (table_element); + rows_current_count = webkit_dom_html_collection_get_length (rows); + + if (rows_current_count < 1) { + g_clear_object (&rows); + return; + } + + row = WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (webkit_dom_html_collection_item (rows, 0)); + cells = webkit_dom_html_table_row_element_get_cells (row); + cells_current_count = webkit_dom_html_collection_get_length (cells); + g_object_unref (row); + + if (rows_current_count < expected_count) { + for (ii = 0; ii < expected_count - rows_current_count; ii++) { + WebKitDOMHTMLElement *new_row; + gulong jj; + + new_row = webkit_dom_html_table_element_insert_row ( + table_element, -1, NULL); + + for (jj = 0; jj < cells_current_count; jj++) + webkit_dom_html_table_row_element_insert_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL); + } + } else if (rows_current_count > expected_count) { + for (ii = 0; ii < rows_current_count - expected_count; ii++) { + webkit_dom_html_table_element_delete_row ( + table_element, -1, NULL); + } + } + g_clear_object (&cells); + g_clear_object (&rows); +} + +gulong +e_dialogs_dom_table_get_row_count (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLTableElement *table_element; + WebKitDOMHTMLCollection *rows = NULL; + glong count; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + document = e_editor_page_get_document (editor_page); + + table_element = get_current_table_element (document); + if (!table_element) + return 0; + + rows = webkit_dom_html_table_element_get_rows (table_element); + + count = webkit_dom_html_collection_get_length (rows); + g_clear_object (&rows); + + return count; +} + +void +e_dialogs_dom_table_set_column_count (EEditorPage *editor_page, + gulong expected_columns) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLTableElement *table_element; + WebKitDOMHTMLCollection *rows = NULL; + gulong ii, row_count; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_element = get_current_table_element (document); + if (!table_element) + return; + + rows = webkit_dom_html_table_element_get_rows (table_element); + row_count = webkit_dom_html_collection_get_length (rows); + + for (ii = 0; ii < row_count; ii++) { + WebKitDOMHTMLTableRowElement *row; + WebKitDOMHTMLCollection *cells = NULL; + gulong jj, current_columns; + + row = WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT ( + webkit_dom_html_collection_item (rows, ii)); + + cells = webkit_dom_html_table_row_element_get_cells (row); + current_columns = webkit_dom_html_collection_get_length (cells); + + if (current_columns < expected_columns) { + for (jj = 0; jj < expected_columns - current_columns; jj++) { + webkit_dom_html_table_row_element_insert_cell ( + row, -1, NULL); + } + } else if (expected_columns < current_columns) { + for (jj = 0; jj < current_columns - expected_columns; jj++) { + webkit_dom_html_table_row_element_delete_cell ( + row, -1, NULL); + } + } + g_object_unref (row); + g_clear_object (&cells); + } + g_clear_object (&rows); +} + +gulong +e_dialogs_dom_table_get_column_count (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLTableElement *table_element; + WebKitDOMHTMLCollection *rows = NULL, *columns = NULL; + WebKitDOMNode *row; + glong count; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + document = e_editor_page_get_document (editor_page); + + table_element = get_current_table_element (document); + if (!table_element) + return 0; + + rows = webkit_dom_html_table_element_get_rows (table_element); + row = webkit_dom_html_collection_item (rows, 0); + + columns = webkit_dom_html_table_row_element_get_cells ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + + count = webkit_dom_html_collection_get_length (columns); + + g_object_unref (row); + g_clear_object (&rows); + g_clear_object (&columns); + + return count; +} + +static WebKitDOMElement * +create_table (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *table, *br, *caret, *element, *cell; + WebKitDOMNode *clone; + gboolean empty = FALSE; + gchar *text_content; + gint i; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + + /* Default 3x3 table */ + table = webkit_dom_document_create_element (document, "TABLE", NULL); + for (i = 0; i < 3; i++) { + WebKitDOMHTMLElement *row; + gint j; + + row = webkit_dom_html_table_element_insert_row ( + WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL); + + for (j = 0; j < 3; j++) { + webkit_dom_html_table_row_element_insert_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL); + } + } + + webkit_dom_element_set_id (table, "-x-evo-current-table"); + + e_editor_dom_selection_save (editor_page); + caret = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + + element = get_parent_block_element (WEBKIT_DOM_NODE (caret)); + text_content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (element)); + empty = text_content && !*text_content; + g_free (text_content); + + clone = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element), FALSE, NULL); + br = webkit_dom_document_create_element (document, "BR", NULL); + webkit_dom_node_append_child (clone, WEBKIT_DOM_NODE (br), NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + clone, + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)), + NULL); + + /* Move caret to the first cell */ + cell = webkit_dom_element_query_selector (table, "td", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (cell), WEBKIT_DOM_NODE (caret), NULL); + caret = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (cell), + WEBKIT_DOM_NODE (caret), + webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (cell)), + NULL); + + /* Insert the table into body unred the current block (if current block is not empty) + * otherwise replace the current block. */ + if (empty) { + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + WEBKIT_DOM_NODE (table), + WEBKIT_DOM_NODE (element), + NULL); + } else { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + WEBKIT_DOM_NODE (table), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)), + NULL); + } + + e_editor_dom_selection_restore (editor_page); + + e_editor_page_emit_content_changed (editor_page); + + return table; +} + +gboolean +e_dialogs_dom_table_show (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMElement *table = NULL; + EEditorUndoRedoManager *manager; + gboolean created = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) { + WebKitDOMRange *range = NULL; + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + table = dom_node_find_parent_element ( + webkit_dom_range_get_start_container (range, NULL), "TABLE"); + g_clear_object (&range); + + if (table) { + webkit_dom_element_set_id (table, "-x-evo-current-table"); + } else { + table = create_table (editor_page); + created = TRUE; + } + } + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + EEditorHistoryEvent *ev; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_TABLE_DIALOG; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + if (!created) + ev->data.dom.from = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (table), TRUE, NULL); + else + ev->data.dom.from = NULL; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + g_clear_object (&dom_selection); + + return created; +} + +void +e_dialogs_dom_table_save_history_on_exit (EEditorPage *editor_page) +{ + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + WebKitDOMDocument *document; + WebKitDOMElement *element; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + element = WEBKIT_DOM_ELEMENT (get_current_table_element (document)); + g_return_if_fail (element != NULL); + + webkit_dom_element_remove_attribute (element, "id"); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + ev->data.dom.to = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (element), TRUE, NULL); + + if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) + e_editor_undo_redo_manager_remove_current_history_event (manager); + else + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); +} diff --git a/modules/webkit-editor/web-extension/e-dialogs-dom-functions.h b/modules/webkit-editor/web-extension/e-dialogs-dom-functions.h new file mode 100644 index 0000000..db28b49 --- /dev/null +++ b/modules/webkit-editor/web-extension/e-dialogs-dom-functions.h @@ -0,0 +1,134 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef E_DIALOGS_DOM_FUNCTIONS_H +#define E_DIALOGS_DOM_FUNCTIONS_H + +#include <webkit2/webkit-web-extension.h> + +#include "e-editor-page.h" + +G_BEGIN_DECLS + +/* ******************** Cell Dialog ***************** */ + +void e_dialogs_dom_cell_mark_current_cell_element + (EEditorPage *editor_page, + const gchar *id); +void e_dialogs_dom_cell_save_history_on_exit + (EEditorPage *editor_page); +void e_dialogs_dom_cell_set_element_v_align + (EEditorPage *editor_page, + const gchar *v_align, + guint scope); +void e_dialogs_dom_cell_set_element_align + (EEditorPage *editor_page, + const gchar *align, + guint scope); +void e_dialogs_dom_cell_set_element_no_wrap + (EEditorPage *editor_page, + gboolean wrap_text, + guint scope); +void e_dialogs_dom_cell_set_element_header_style + (EEditorPage *editor_page, + gboolean header_style, + guint scope); +void e_dialogs_dom_cell_set_element_width + (EEditorPage *editor_page, + const gchar *width, + guint scope); +void e_dialogs_dom_cell_set_element_col_span + (EEditorPage *editor_page, + glong span, + guint scope); +void e_dialogs_dom_cell_set_element_row_span + (EEditorPage *editor_page, + glong span, + guint scope); +void e_dialogs_dom_cell_set_element_bg_color + (EEditorPage *editor_page, + const gchar *color, + guint scope); + +/* ******************** HRule Dialog ***************** */ + +gboolean e_dialogs_dom_h_rule_find_hrule (EEditorPage *editor_page); +void e_dialogs_dom_h_rule_dialog_on_close + (EEditorPage *editor_page); + +/* ******************** Image Dialog ***************** */ + +void e_dialogs_dom_image_mark_image (EEditorPage *editor_page); +void e_dialogs_dom_image_save_history_on_exit + (EEditorPage *editor_page); +void e_dialogs_dom_image_set_element_url + (EEditorPage *editor_page, + const gchar *url); +gchar * e_dialogs_dom_image_get_element_url + (EEditorPage *editor_page); + +/* ******************** Link Dialog ***************** */ + +void e_dialogs_dom_link_commit (EEditorPage *editor_page, + const gchar *url, + const gchar *inner_text); +GVariant * e_dialogs_dom_link_show (EEditorPage *editor_page); +void e_dialogs_dom_link_dialog_on_open + (EEditorPage *editor_page); +void e_dialogs_dom_link_dialog_on_close + (EEditorPage *editor_page); + +/* ******************** Page Dialog ***************** */ + +void e_dialogs_dom_page_save_history (EEditorPage *editor_page); +void e_dialogs_dom_page_save_history_on_exit + (EEditorPage *editor_page); + +/* ******************** Spell Check Dialog ***************** */ + +gchar * e_dialogs_dom_spell_check_prev (EEditorPage *editor_page, + const gchar *from_word, + const gchar * const *languages); + +gchar * e_dialogs_dom_spell_check_next (EEditorPage *editor_page, + const gchar *from_word, + const gchar * const *languages); + +/* ******************** Table Dialog ***************** */ + +void e_dialogs_dom_table_set_row_count + (EEditorPage *editor_page, + gulong expected_count); + +gulong e_dialogs_dom_table_get_row_count + (EEditorPage *editor_page); + +void e_dialogs_dom_table_set_column_count + (EEditorPage *editor_page, + gulong expected_columns); + +gulong e_dialogs_dom_table_get_column_count + (EEditorPage *editor_page); + +gboolean e_dialogs_dom_table_show (EEditorPage *editor_page); + +void e_dialogs_dom_table_save_history_on_exit + (EEditorPage *editor_page); + +G_END_DECLS + +#endif /* E_DIALOGS_DOM_FUNCTIONS_H */ diff --git a/modules/webkit-editor/web-extension/e-editor-dom-functions.c b/modules/webkit-editor/web-extension/e-editor-dom-functions.c new file mode 100644 index 0000000..ecc833d --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-dom-functions.c @@ -0,0 +1,17386 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#define WEBKIT_DOM_USE_UNSTABLE_API +#include <webkitdom/WebKitDOMDocumentUnstable.h> +#include <webkitdom/WebKitDOMDocumentFragmentUnstable.h> +#include <webkitdom/WebKitDOMDOMSelection.h> +#include <webkitdom/WebKitDOMDOMWindowUnstable.h> +#include <webkitdom/WebKitDOMHTMLElementUnstable.h> +#include <webkitdom/WebKitDOMRangeUnstable.h> +#undef WEBKIT_DOM_USE_UNSTABLE_API + +#include "web-extensions/e-dom-utils.h" + +#include "e-editor-page.h" +#include "e-editor-undo-redo-manager.h" + +#include "e-editor-dom-functions.h" + +#define HTML_KEY_CODE_BACKSPACE 8 +#define HTML_KEY_CODE_RETURN 13 +#define HTML_KEY_CODE_CONTROL 17 +#define HTML_KEY_CODE_SPACE 32 +#define HTML_KEY_CODE_DELETE 46 +#define HTML_KEY_CODE_TABULATOR 9 + +/* ******************** Tests ******************** */ + +static gchar * +workaround_spaces (const gchar *text) +{ + GString *tmp; + gchar *str = NULL; + + tmp = e_str_replace_string (text, " ", " "); + if (tmp) { + str = g_string_free (tmp, FALSE); + text = str; + } + + tmp = e_str_replace_string (text, " ", " "); + if (tmp) { + g_free (str); + str = g_string_free (tmp, FALSE); + } else if (!str) { + str = g_strdup (text); + } + + return str; +} + +gboolean +e_editor_dom_test_html_equal (WebKitDOMDocument *document, + const gchar *html1, + const gchar *html2) +{ + WebKitDOMElement *elem1, *elem2; + gchar *str1, *str2; + gboolean res = FALSE; + GError *error = NULL; + + g_return_val_if_fail (WEBKIT_DOM_IS_DOCUMENT (document), FALSE); + g_return_val_if_fail (html1 != NULL, FALSE); + g_return_val_if_fail (html2 != NULL, FALSE); + + elem1 = webkit_dom_document_create_element (document, "TestHtmlEqual", &error); + if (error || !elem1) { + g_warning ("%s: Failed to create elem1: %s", G_STRFUNC, error ? error->message : "Unknown error"); + g_clear_error (&error); + return FALSE; + } + + elem2 = webkit_dom_document_create_element (document, "TestHtmlEqual", &error); + if (error || !elem2) { + g_warning ("%s: Failed to create elem2: %s", G_STRFUNC, error ? error->message : "Unknown error"); + g_clear_error (&error); + return FALSE; + } + + /* FIXME WK2: Workaround when is used instead of regular spaces. (Placed by WebKit?) */ + str1 = workaround_spaces (html1); + str2 = workaround_spaces (html2); + + webkit_dom_element_set_inner_html (elem1, str1, &error); + if (!error) { + webkit_dom_element_set_inner_html (elem2, str2, &error); + + if (!error) { + webkit_dom_node_normalize (WEBKIT_DOM_NODE (elem1)); + webkit_dom_node_normalize (WEBKIT_DOM_NODE (elem2)); + + res = webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (elem1), WEBKIT_DOM_NODE (elem2)); + } else { + g_warning ("%s: Failed to set inner html2: %s", G_STRFUNC, error->message); + } + } else { + g_warning ("%s: Failed to set inner html1: %s", G_STRFUNC, error->message); + } + + if (res && (g_strcmp0 (html1, str1) != 0 || g_strcmp0 (html2, str2) != 0)) + g_warning ("%s: Applied the ' ' workaround", G_STRFUNC); + + g_clear_error (&error); + g_free (str1); + g_free (str2); + + return res; +} + +/* ******************** Actions ******************** */ + +static WebKitDOMElement * +get_table_cell_element (WebKitDOMDocument *document) +{ + return webkit_dom_document_get_element_by_id (document, "-x-evo-current-cell"); +} + +static void +prepare_history_for_table (EEditorPage *editor_page, + WebKitDOMElement *table, + EEditorHistoryEvent *ev) +{ + ev->type = HISTORY_TABLE_DIALOG; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + + ev->data.dom.from = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (table), TRUE, NULL); +} + + +static void +save_history_for_table (EEditorPage *editor_page, + WebKitDOMElement *table, + EEditorHistoryEvent *ev) +{ + EEditorUndoRedoManager *manager; + + if (table) + ev->data.dom.to = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (table), TRUE, NULL); + else + ev->data.dom.to = NULL; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + e_editor_undo_redo_manager_insert_history_event (manager, ev); +} + +void +e_editor_dom_delete_cell_contents (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNode *node; + WebKitDOMElement *cell, *table_cell, *table; + EEditorHistoryEvent *ev = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_cell = get_table_cell_element (document); + g_return_if_fail (table_cell != NULL); + + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TD"); + if (!cell) + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH"); + g_return_if_fail (cell != NULL); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE"); + g_return_if_fail (table != NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + prepare_history_for_table (editor_page, table, ev); + + while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (cell)))) + remove_node (node); + + save_history_for_table (editor_page, table, ev); +} + +void +e_editor_dom_delete_column (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *cell, *table, *table_cell; + WebKitDOMHTMLCollection *rows = NULL; + EEditorHistoryEvent *ev = NULL; + gulong index, length, ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_cell = get_table_cell_element (document); + g_return_if_fail (table_cell != NULL); + + /* Find TD in which the selection starts */ + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TD"); + if (!cell) + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH"); + g_return_if_fail (cell != NULL); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE"); + g_return_if_fail (table != NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + prepare_history_for_table (editor_page, table, ev); + + rows = webkit_dom_html_table_element_get_rows ( + WEBKIT_DOM_HTML_TABLE_ELEMENT (table)); + length = webkit_dom_html_collection_get_length (rows); + + index = webkit_dom_html_table_cell_element_get_cell_index ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell)); + + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *row; + + row = webkit_dom_html_collection_item (rows, ii); + + webkit_dom_html_table_row_element_delete_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index, NULL); + g_object_unref (row); + } + + g_clear_object (&rows); + + save_history_for_table (editor_page, table, ev); +} + +void +e_editor_dom_delete_row (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *row, *table, *table_cell; + EEditorHistoryEvent *ev = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_cell = get_table_cell_element (document); + g_return_if_fail (table_cell != NULL); + + row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR"); + g_return_if_fail (row != NULL); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE"); + g_return_if_fail (table != NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + prepare_history_for_table (editor_page, table, ev); + + remove_node (WEBKIT_DOM_NODE (row)); + + save_history_for_table (editor_page, table, ev); +} + +void +e_editor_dom_delete_table (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *table, *table_cell; + EEditorHistoryEvent *ev = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_cell = get_table_cell_element (document); + g_return_if_fail (table_cell != NULL); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE"); + g_return_if_fail (table != NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + prepare_history_for_table (editor_page, table, ev); + + remove_node (WEBKIT_DOM_NODE (table)); + + save_history_for_table (editor_page, NULL, ev); +} + +void +e_editor_dom_insert_column_after (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *cell, *row, *table_cell, *table; + EEditorHistoryEvent *ev = NULL; + gulong index; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_cell = get_table_cell_element (document); + g_return_if_fail (table_cell != NULL); + + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TD"); + if (!cell) + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH"); + g_return_if_fail (cell != NULL); + + row = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR"); + g_return_if_fail (row != NULL); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE"); + g_return_if_fail (table != NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + prepare_history_for_table (editor_page, table, ev); + + /* Get the first row in the table */ + row = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_get_first_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)))); + + index = webkit_dom_html_table_cell_element_get_cell_index ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell)); + + while (row) { + webkit_dom_html_table_row_element_insert_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index + 1, NULL); + + row = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row))); + } + + save_history_for_table (editor_page, table, ev); +} + +void +e_editor_dom_insert_column_before (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *cell, *row, *table_cell, *table; + EEditorHistoryEvent *ev = NULL; + gulong index; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_cell = get_table_cell_element (document); + g_return_if_fail (table_cell != NULL); + + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TD"); + if (!cell) { + cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH"); + } + g_return_if_fail (cell != NULL); + + row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR"); + g_return_if_fail (row != NULL); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE"); + g_return_if_fail (table != NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + prepare_history_for_table (editor_page, table, ev); + + /* Get the first row in the table */ + row = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_get_first_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)))); + + index = webkit_dom_html_table_cell_element_get_cell_index ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell)); + + while (row) { + webkit_dom_html_table_row_element_insert_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index - 1, NULL); + + row = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row))); + } + + save_history_for_table (editor_page, table, ev); +} + +void +e_editor_dom_insert_row_above (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *row, *table, *table_cell; + WebKitDOMHTMLCollection *cells = NULL; + WebKitDOMHTMLElement *new_row; + EEditorHistoryEvent *ev = NULL; + gulong index, cell_count, ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_cell = get_table_cell_element (document); + g_return_if_fail (table_cell != NULL); + + row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR"); + g_return_if_fail (row != NULL); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE"); + g_return_if_fail (table != NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + prepare_history_for_table (editor_page, table, ev); + + index = webkit_dom_html_table_row_element_get_row_index ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + + new_row = webkit_dom_html_table_element_insert_row ( + WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index, NULL); + + cells = webkit_dom_html_table_row_element_get_cells ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + cell_count = webkit_dom_html_collection_get_length (cells); + for (ii = 0; ii < cell_count; ii++) { + webkit_dom_html_table_row_element_insert_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL); + } + + g_clear_object (&cells); + + save_history_for_table (editor_page, table, ev); +} + +void +e_editor_dom_insert_row_below (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *row, *table, *table_cell; + WebKitDOMHTMLCollection *cells = NULL; + WebKitDOMHTMLElement *new_row; + EEditorHistoryEvent *ev = NULL; + gulong index, cell_count, ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + table_cell = get_table_cell_element (document); + g_return_if_fail (table_cell != NULL); + + row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR"); + g_return_if_fail (row != NULL); + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE"); + g_return_if_fail (table != NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + prepare_history_for_table (editor_page, table, ev); + + index = webkit_dom_html_table_row_element_get_row_index ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + + new_row = webkit_dom_html_table_element_insert_row ( + WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index + 1, NULL); + + cells = webkit_dom_html_table_row_element_get_cells ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row)); + cell_count = webkit_dom_html_collection_get_length (cells); + for (ii = 0; ii < cell_count; ii++) { + webkit_dom_html_table_row_element_insert_cell ( + WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL); + } + + g_clear_object (&cells); + + save_history_for_table (editor_page, table, ev); +} + +void +e_editor_dom_save_history_for_cut (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + EEditorHistoryEvent *ev; + EEditorUndoRedoManager *manager; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (!webkit_dom_dom_selection_get_range_count (dom_selection) || + webkit_dom_dom_selection_get_is_collapsed (dom_selection)) { + g_clear_object (&dom_selection); + return; + } + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_DELETE; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->after.start.x = ev->before.start.x; + ev->after.start.y = ev->before.start.y; + ev->after.end.x = ev->before.start.x; + ev->after.end.y = ev->before.start.y; + + /* Save the fragment. */ + fragment = webkit_dom_range_clone_contents (range, NULL); + g_clear_object (&dom_selection); + g_clear_object (&range); + ev->data.fragment = g_object_ref (fragment); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + e_editor_page_set_dont_save_history_in_body_input (editor_page, TRUE); +} + +/* ******************** View ******************** */ + +/* + * e_editor_dom_exec_command: + * @document: a #WebKitDOMDocument + * @command: an #EContentEditorCommand to execute + * @value: value of the command (or @NULL if the command does not require value) + * + * The function will fail when @value is @NULL or empty but the current @command + * requires a value to be passed. The @value is ignored when the @command does + * not expect any value. + * + * Returns: @TRUE when the command was succesfully executed, @FALSE otherwise. + */ +gboolean +e_editor_dom_exec_command (EEditorPage *editor_page, + EContentEditorCommand command, + const gchar *value) +{ + const gchar *cmd_str = 0; + gboolean has_value = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + +#define CHECK_COMMAND(cmd,str,val) case cmd:\ + if (val) {\ + g_return_val_if_fail (value && *value, FALSE);\ + }\ + has_value = val; \ + cmd_str = str;\ + break; + + switch (command) { + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR, "BackColor", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_BOLD, "Bold", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_COPY, "Copy", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_CREATE_LINK, "CreateLink", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_CUT, "Cut", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, "DefaultParagraphSeparator", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_DELETE, "Delete", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FIND_STRING, "FindString", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_NAME, "FontName", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_SIZE, "FontSize", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_SIZE_DELTA, "FontSizeDelta", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORE_COLOR, "ForeColor", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORMAT_BLOCK, "FormatBlock", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE, "ForwardDelete", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_HILITE_COLOR, "HiliteColor", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INDENT, "Indent", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_HORIZONTAL_RULE, "InsertHorizontalRule", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_HTML, "InsertHTML", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_IMAGE, "InsertImage", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_LINE_BREAK, "InsertLineBreak", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, "InsertNewlineInQuotedContent", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_ORDERED_LIST, "InsertOrderedList", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_PARAGRAPH, "InsertParagraph", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, "InsertText", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_UNORDERED_LIST, "InsertUnorderedList", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_ITALIC, "Italic", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_CENTER, "JustifyCenter", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_FULL, "JustifyFull", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_LEFT, "JustifyLeft", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_NONE, "JustifyNone", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_RIGHT, "JustifyRight", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_OUTDENT, "Outdent", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE, "Paste", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE_AND_MATCH_STYLE, "PasteAndMatchStyle", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE_AS_PLAIN_TEXT, "PasteAsPlainText", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PRINT, "Print", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_REDO, "Redo", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_REMOVE_FORMAT, "RemoveFormat", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SELECT_ALL, "SelectAll", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH, "Strikethrough", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS, "StyleWithCSS", TRUE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SUBSCRIPT, "Subscript", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT, "Superscript", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_TRANSPOSE, "Transpose", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNDERLINE, "Underline", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNDO, "Undo", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNLINK, "Unlink", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNSELECT, "Unselect", FALSE) + CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_USE_CSS, "UseCSS", TRUE) + } + + e_editor_page_set_dont_save_history_in_body_input (editor_page, TRUE); + + return webkit_dom_document_exec_command ( + e_editor_page_get_document (editor_page), cmd_str, FALSE, has_value ? value : "" ); +} + +static void +perform_spell_check (WebKitDOMDOMSelection *dom_selection, + WebKitDOMRange *start_range, + WebKitDOMRange *end_range) +{ + WebKitDOMRange *actual = start_range; + + /* FIXME WK2: this doesn't work, the cursor is moved, but the spellcheck is not updated */ + /* Go through all words to spellcheck them. To avoid this we have to wait for + * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */ + /* We are moving forward word by word until we hit the text on the end. */ + while (actual && webkit_dom_range_compare_boundary_points (end_range, WEBKIT_DOM_RANGE_END_TO_END, actual, NULL) != 0) { + if (actual != start_range) + g_object_unref (actual); + webkit_dom_dom_selection_modify ( + dom_selection, "move", "forward", "word"); + actual = webkit_dom_dom_selection_get_range_at ( + dom_selection, 0, NULL); + } + g_clear_object (&actual); +} + +void +e_editor_dom_force_spell_check_for_current_paragraph (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMElement *parent, *element; + WebKitDOMRange *end_range = NULL, *actual = NULL; + WebKitDOMText *text; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + if (!e_editor_page_get_inline_spelling_enabled (editor_page)) + return; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_query_selector ( + document, "body[spellcheck=true]", NULL); + + if (!element) + return; + + if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element))) + return; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + if (!selection_start_marker || !selection_end_marker) + return; + + /* Block callbacks of selection-changed signal as we don't want to + * recount all the block format things in EEditorSelection and here as well + * when we are moving with caret */ + e_editor_page_block_selection_changed (editor_page); + + parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_end_marker)); + + /* Append some text on the end of the element */ + text = webkit_dom_document_create_text_node (document, "-x-evo-end"); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (parent), + WEBKIT_DOM_NODE (text), + NULL); + + parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker)); + + /* Create range that's pointing on the end of this text */ + end_range = webkit_dom_document_create_range (document); + webkit_dom_range_select_node_contents ( + end_range, WEBKIT_DOM_NODE (text), NULL); + webkit_dom_range_collapse (end_range, FALSE, NULL); + + /* Move on the beginning of the paragraph */ + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + + actual = webkit_dom_document_create_range (document); + webkit_dom_range_select_node_contents ( + actual, WEBKIT_DOM_NODE (parent), NULL); + webkit_dom_range_collapse (actual, TRUE, NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, actual); + g_clear_object (&actual); + + actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + perform_spell_check (dom_selection, actual, end_range); + + g_clear_object (&dom_selection); + g_clear_object (&dom_window); + g_clear_object (&end_range); + g_clear_object (&actual); + + /* Remove the text that we inserted on the end of the paragraph */ + remove_node (WEBKIT_DOM_NODE (text)); + + /* Unblock the callbacks */ + e_editor_page_unblock_selection_changed (editor_page); + + e_editor_dom_selection_restore (editor_page); +} + +static void +refresh_spell_check (EEditorPage *editor_page, + gboolean enable_spell_check) +{ + WebKitDOMDocument *document; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMHTMLElement *body; + WebKitDOMRange *end_range = NULL, *actual = NULL; + WebKitDOMText *text; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + body = webkit_dom_document_get_body (document); + + if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))) + return; + + /* Enable/Disable spellcheck in composer */ + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body), + "spellcheck", + enable_spell_check ? "true" : "false", + NULL); + webkit_dom_html_element_set_spellcheck (body, FALSE); + webkit_dom_html_element_set_spellcheck (body, enable_spell_check); + return; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + /* Sometimes the web view is not focused, so we have to save the selection + * manually into the body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMNode *child; + + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + if (!WEBKIT_DOM_IS_HTML_ELEMENT (child)) + return; + + dom_add_selection_markers_into_element_start ( + document, + WEBKIT_DOM_ELEMENT (child), + &selection_start_marker, + &selection_end_marker); + } + + /* Block callbacks of selection-changed signal as we don't want to + * recount all the block format things in EEditorSelection and here as well + * when we are moving with caret */ + e_editor_page_block_selection_changed (editor_page); + + /* Append some text on the end of the body */ + text = webkit_dom_document_create_text_node (document, "-x-evo-end"); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL); + + /* Create range that's pointing on the end of this text */ + end_range = webkit_dom_document_create_range (document); + webkit_dom_range_select_node_contents ( + end_range, WEBKIT_DOM_NODE (text), NULL); + webkit_dom_range_collapse (end_range, FALSE, NULL); + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + + /* Move on the beginning of the document */ + webkit_dom_dom_selection_modify ( + dom_selection, "move", "backward", "documentboundary"); + + actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + perform_spell_check (dom_selection, actual, end_range); + + g_clear_object (&dom_selection); + g_clear_object (&dom_window); + g_clear_object (&end_range); + g_clear_object (&actual); + + /* Remove the text that we inserted on the end of the body */ + remove_node (WEBKIT_DOM_NODE (text)); + + /* Unblock the callbacks */ + e_editor_page_unblock_selection_changed (editor_page); + + e_editor_dom_selection_restore (editor_page); +} + +void +e_editor_dom_turn_spell_check_off (EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + refresh_spell_check (editor_page, FALSE); +} + +void +e_editor_dom_force_spell_check_in_viewport (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMElement *last_element; + WebKitDOMHTMLElement *body; + WebKitDOMRange *end_range = NULL, *actual = NULL; + WebKitDOMText *text; + glong viewport_height; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!e_editor_page_get_inline_spelling_enabled (editor_page)) + return; + + document = e_editor_page_get_document (editor_page); + body = WEBKIT_DOM_HTML_ELEMENT (webkit_dom_document_query_selector ( + document, "body[spellcheck=true]", NULL)); + + if (!body) { + body = webkit_dom_document_get_body (document); + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body), "spellcheck", "true", NULL); + } + + if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))) + return; + + e_editor_dom_selection_save (editor_page); + + /* Block callbacks of selection-changed signal as we don't want to + * recount all the block format things in EEditorSelection and here as well + * when we are moving with caret */ + e_editor_page_block_selection_changed (editor_page); + + /* We have to add 10 px offset as otherwise just the HTML element will be returned */ + actual = webkit_dom_document_caret_range_from_point (document, 10, 10); + if (!actual) + goto out; + + /* Append some text on the end of the body */ + text = webkit_dom_document_create_text_node (document, "-x-evo-end"); + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + + /* We have to add 10 px offset as otherwise just the HTML element will be returned */ + viewport_height = webkit_dom_dom_window_get_inner_height (dom_window); + last_element = webkit_dom_document_element_from_point (document, 10, viewport_height - 10); + if (last_element && !WEBKIT_DOM_IS_HTML_HTML_ELEMENT (last_element) && + !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (last_element)) { + WebKitDOMElement *parent; + + parent = get_parent_block_element (WEBKIT_DOM_NODE (last_element)); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (parent), WEBKIT_DOM_NODE (text), NULL); + } else + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL); + + /* Create range that's pointing on the end of viewport */ + end_range = webkit_dom_document_create_range (document); + webkit_dom_range_select_node_contents ( + end_range, WEBKIT_DOM_NODE (text), NULL); + webkit_dom_range_collapse (end_range, FALSE, NULL); + + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, actual); + perform_spell_check (dom_selection, actual, end_range); + + g_clear_object (&dom_selection); + g_clear_object (&dom_window); + g_clear_object (&end_range); + g_clear_object (&actual); + + /* Remove the text that we inserted on the end of the body */ + remove_node (WEBKIT_DOM_NODE (text)); + + out: + /* Unblock the callbacks */ + e_editor_page_unblock_selection_changed (editor_page); + + e_editor_dom_selection_restore (editor_page); +} + +void +e_editor_dom_force_spell_check (EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_page_get_inline_spelling_enabled (editor_page)) + refresh_spell_check (editor_page, TRUE); +} + +gboolean +e_editor_dom_node_is_citation_node (WebKitDOMNode *node) +{ + gchar *value; + + if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) + return FALSE; + + value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type"); + + /* citation == <blockquote type='cite'> */ + if (value && g_strcmp0 (value, "cite") == 0) { + g_free (value); + return TRUE; + } else { + g_free (value); + return FALSE; + } +} + +gint +e_editor_dom_get_citation_level (WebKitDOMNode *node, + gboolean set_plaintext_quoted) +{ + WebKitDOMNode *parent = node; + gint level = 0; + + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && + webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type")) + level++; + + parent = webkit_dom_node_get_parent_node (parent); + } + + return level; +} + +static gchar * +get_quotation_for_level (gint quote_level) +{ + gint ii; + GString *output = g_string_new (""); + + for (ii = 0; ii < quote_level; ii++) { + g_string_append (output, "<span class=\"-x-evo-quote-character\">"); + g_string_append (output, QUOTE_SYMBOL); + g_string_append (output, " "); + g_string_append (output, "</span>"); + } + + return g_string_free (output, FALSE); +} + +void +e_editor_dom_quote_plain_text_element_after_wrapping (EEditorPage *editor_page, + WebKitDOMElement *element, + gint quote_level) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *list = NULL; + WebKitDOMNode *quoted_node; + gint length, ii; + gchar *quotation; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + g_return_if_fail (element != NULL); + + document = e_editor_page_get_document (editor_page); + + quoted_node = WEBKIT_DOM_NODE ( + webkit_dom_document_create_element (document, "SPAN", NULL)); + webkit_dom_element_set_class_name ( + WEBKIT_DOM_ELEMENT (quoted_node), "-x-evo-quoted"); + quotation = get_quotation_for_level (quote_level); + webkit_dom_element_set_inner_html ( + WEBKIT_DOM_ELEMENT (quoted_node), quotation, NULL); + + list = webkit_dom_element_query_selector_all ( + element, "br.-x-evo-wrap-br", NULL); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (element), + quoted_node, + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)), + NULL); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *br = webkit_dom_node_list_item (list, ii); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (br), + webkit_dom_node_clone_node_with_error (quoted_node, TRUE, NULL), + webkit_dom_node_get_next_sibling (br), + NULL); + g_object_unref (br); + } + + g_clear_object (&list); + g_free (quotation); +} + +static gboolean +return_pressed_in_empty_line (EEditorPage *editor_page) +{ + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return FALSE; + + node = webkit_dom_range_get_start_container (range, NULL); + if (!WEBKIT_DOM_IS_TEXT (node)) { + WebKitDOMNode *first_child; + + first_child = webkit_dom_node_get_first_child (node); + if (first_child && WEBKIT_DOM_IS_ELEMENT (first_child) && + element_has_class (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-quoted")) { + WebKitDOMNode *prev_sibling; + + prev_sibling = webkit_dom_node_get_previous_sibling (node); + if (!prev_sibling) { + gboolean collapsed; + + collapsed = webkit_dom_range_get_collapsed (range, NULL); + g_clear_object (&range); + return collapsed; + } + } + } + + g_clear_object (&range); + + return FALSE; +} + +WebKitDOMNode * +e_editor_dom_get_parent_block_node_from_child (WebKitDOMNode *node) +{ + WebKitDOMNode *parent = node; + + if (!WEBKIT_DOM_IS_ELEMENT (parent) || + e_editor_dom_is_selection_position_node (parent)) + parent = webkit_dom_node_get_parent_node (parent); + + if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted") || + element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quote-character") || + element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-signature") || + element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-resizable-wrapper") || + WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) || + element_has_tag (WEBKIT_DOM_ELEMENT (parent), "b") || + element_has_tag (WEBKIT_DOM_ELEMENT (parent), "i") || + element_has_tag (WEBKIT_DOM_ELEMENT (parent), "u")) + parent = webkit_dom_node_get_parent_node (parent); + + if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted") || + element_has_class (WEBKIT_DOM_ELEMENT (parent), "Apple-tab-span") || + element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-resizable-wrapper")) + parent = webkit_dom_node_get_parent_node (parent); + + return parent; +} + +WebKitDOMElement * +e_editor_dom_wrap_and_quote_element (EEditorPage *editor_page, + WebKitDOMElement *element) +{ + gint citation_level; + WebKitDOMElement *tmp_element = element; + + g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (element), element); + + if (e_editor_page_get_html_mode (editor_page)) + return element; + + citation_level = e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element), FALSE); + + e_editor_dom_remove_quoting_from_element (element); + e_editor_dom_remove_wrapping_from_element (element); + + if (WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (element) && + webkit_dom_element_has_attribute (element, "data-evo-paragraph")) { + gint word_wrap_length, length; + + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + length = word_wrap_length - 2 * citation_level; + tmp_element = e_editor_dom_wrap_paragraph_length ( + editor_page, element, length); + } + + if (citation_level > 0) { + + webkit_dom_node_normalize (WEBKIT_DOM_NODE (tmp_element)); + e_editor_dom_quote_plain_text_element_after_wrapping ( + editor_page, tmp_element, citation_level); + } + + return tmp_element; +} + +WebKitDOMElement * +e_editor_dom_insert_new_line_into_citation (EEditorPage *editor_page, + const gchar *html_to_insert) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element, *paragraph = NULL; + WebKitDOMNode *last_block; + gboolean html_mode = FALSE, ret_val, avoid_editor_call; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + html_mode = e_editor_page_get_html_mode (editor_page); + + avoid_editor_call = return_pressed_in_empty_line (editor_page); + + if (avoid_editor_call) { + WebKitDOMElement *selection_start_marker; + WebKitDOMNode *current_block, *parent, *parent_block, *block_clone; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + current_block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + block_clone = webkit_dom_node_clone_node_with_error (current_block, TRUE, NULL); + /* Find selection start marker and restore it after the new line + * is inserted */ + selection_start_marker = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block_clone), "#-x-evo-selection-start-marker", NULL); + + /* Find parent node that is immediate child of the BODY */ + /* Build the same structure of parent nodes of the current block */ + parent_block = current_block; + parent = webkit_dom_node_get_parent_node (parent_block); + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + WebKitDOMNode *node; + + parent_block = parent; + node = webkit_dom_node_clone_node_with_error (parent_block, FALSE, NULL); + webkit_dom_node_append_child (node, block_clone, NULL); + block_clone = node; + parent = webkit_dom_node_get_parent_node (parent_block); + } + + paragraph = e_editor_dom_get_paragraph_element (editor_page, -1, 0); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (paragraph), + WEBKIT_DOM_NODE ( + webkit_dom_document_create_element (document, "BR", NULL)), + NULL); + + /* Insert the selection markers to right place */ + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (paragraph), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker)), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph)), + NULL); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (paragraph), + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph)), + NULL); + + /* Insert the cloned nodes before the BODY parent node */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent_block), + block_clone, + parent_block, + NULL); + + /* Insert the new empty paragraph before the BODY parent node */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent_block), + WEBKIT_DOM_NODE (paragraph), + parent_block, + NULL); + + /* Remove the old block (its copy was moved to the right place) */ + remove_node (current_block); + + e_editor_dom_selection_restore (editor_page); + + return NULL; + } else { + e_editor_dom_remove_input_event_listener_from_body (editor_page); + e_editor_page_block_selection_changed (editor_page); + + ret_val = e_editor_dom_exec_command ( + editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL); + + e_editor_page_unblock_selection_changed (editor_page); + e_editor_dom_register_input_event_listener_on_body (editor_page); + + if (!ret_val) + return NULL; + + element = webkit_dom_document_query_selector ( + document, "body>br", NULL); + + if (!element) + return NULL; + } + + last_block = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + while (last_block && e_editor_dom_node_is_citation_node (last_block)) + last_block = webkit_dom_node_get_last_child (last_block); + + if (last_block) { + WebKitDOMNode *last_child; + + if ((last_child = webkit_dom_node_get_last_child (last_block))) { + if (WEBKIT_DOM_IS_ELEMENT (last_child) && + element_has_class (WEBKIT_DOM_ELEMENT (last_child), "-x-evo-quoted")) + webkit_dom_node_append_child ( + last_block, + WEBKIT_DOM_NODE ( + webkit_dom_document_create_element ( + document, "br", NULL)), + NULL); + } + } + + if (!html_mode) { + WebKitDOMNode *sibling; + + sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + + if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (sibling)) { + WebKitDOMNode *node; + + node = webkit_dom_node_get_first_child (sibling); + while (node && e_editor_dom_node_is_citation_node (node)) + node = webkit_dom_node_get_first_child (node); + + /* Rewrap and requote nodes that were created by split. */ + if (WEBKIT_DOM_IS_ELEMENT (node)) + e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (node)); + + if (WEBKIT_DOM_IS_ELEMENT (last_block)) + e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (last_block)); + + e_editor_dom_force_spell_check_in_viewport (editor_page); + } + } + + if (html_to_insert && *html_to_insert) { + paragraph = e_editor_dom_prepare_paragraph (editor_page, FALSE); + webkit_dom_element_set_inner_html ( + paragraph, html_to_insert, NULL); + if (!webkit_dom_element_query_selector (paragraph, "#-x-evo-selection-start-marker", NULL)) + dom_add_selection_markers_into_element_end ( + document, paragraph, NULL, NULL); + } else + paragraph = e_editor_dom_prepare_paragraph (editor_page, TRUE); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + WEBKIT_DOM_NODE (paragraph), + WEBKIT_DOM_NODE (element), + NULL); + + remove_node (WEBKIT_DOM_NODE (element)); + + e_editor_dom_selection_restore (editor_page); + + return paragraph; +} + +/* For purpose of this function see e-mail-formatter-quote.c */ +static void +put_body_in_citation (WebKitDOMDocument *document) +{ + WebKitDOMElement *cite_body = webkit_dom_document_query_selector ( + document, "span.-x-evo-cite-body", NULL); + + if (cite_body) { + WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document); + WebKitDOMNode *citation; + WebKitDOMNode *sibling; + + citation = WEBKIT_DOM_NODE ( + webkit_dom_document_create_element (document, "blockquote", NULL)); + webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (citation), "-x-evo-main-cite"); + webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (citation), "type", "cite", NULL); + + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + citation, + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), + NULL); + + while ((sibling = webkit_dom_node_get_next_sibling (citation))) + webkit_dom_node_append_child (citation, sibling, NULL); + + remove_node (WEBKIT_DOM_NODE (cite_body)); + } +} + +/* For purpose of this function see e-mail-formatter-quote.c */ +static void +move_elements_to_body (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + WebKitDOMNodeList *list = NULL; + gint ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + body = webkit_dom_document_get_body (document); + list = webkit_dom_document_query_selector_all ( + document, "div[data-headers]", NULL); + for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (node), "data-headers"); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + node, + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (body)), + NULL); + + g_object_unref (node); + } + g_clear_object (&list); + + list = webkit_dom_document_query_selector_all ( + document, "span.-x-evo-to-body[data-credits]", NULL); + for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) { + char *credits; + WebKitDOMElement *element; + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + element = e_editor_dom_get_paragraph_element (editor_page, -1, 0); + credits = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "data-credits"); + if (credits) + webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element), credits, NULL); + g_free (credits); + + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (element), + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (body)), + NULL); + + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); +} + +static void +repair_gmail_blockquotes (WebKitDOMDocument *document) +{ + WebKitDOMNodeList *list = NULL; + gint ii, length; + + list = webkit_dom_document_query_selector_all ( + document, "blockquote.gmail_quote", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "class"); + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "style"); + webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node), "type", "cite", NULL); + + if (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_last_child (node))) + webkit_dom_node_append_child ( + node, + WEBKIT_DOM_NODE ( + webkit_dom_document_create_element ( + document, "br", NULL)), + NULL); + g_object_unref (node); + } + g_clear_object (&list); +} + +static void +remove_thunderbird_signature (WebKitDOMDocument *document) +{ + WebKitDOMElement *signature; + + signature = webkit_dom_document_query_selector ( + document, "pre.moz-signature", NULL); + if (signature) + remove_node (WEBKIT_DOM_NODE (signature)); +} + +void +e_editor_dom_check_magic_links (EEditorPage *editor_page, + gboolean include_space_by_user) +{ + WebKitDOMDocument *document; + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + gchar *node_text; + gchar **urls; + gboolean include_space = FALSE; + gboolean is_email_address = FALSE; + gboolean return_key_pressed; + GRegex *regex = NULL; + GMatchInfo *match_info; + gint start_pos_url, end_pos_url; + gboolean has_selection; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!e_editor_page_get_magic_links_enabled (editor_page)) + return; + + return_key_pressed = e_editor_page_get_return_key_pressed (editor_page); + document = e_editor_page_get_document (editor_page); + + if (include_space_by_user) + include_space = TRUE; + else + include_space = e_editor_page_get_space_key_pressed (editor_page); + + range = e_editor_dom_get_current_range (editor_page); + node = webkit_dom_range_get_end_container (range, NULL); + has_selection = !webkit_dom_range_get_collapsed (range, NULL); + g_clear_object (&range); + + if (return_key_pressed) { + WebKitDOMNode* block; + + block = e_editor_dom_get_parent_block_node_from_child (node); + /* Get previous block */ + if (!(block = webkit_dom_node_get_previous_sibling (block))) + return; + + /* If block is quoted content, get the last block there */ + while (block && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (block)) + block = webkit_dom_node_get_last_child (block); + + /* Get the last non-empty node */ + node = webkit_dom_node_get_last_child (block); + if (WEBKIT_DOM_IS_CHARACTER_DATA (node) && + webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)) == 0) + node = webkit_dom_node_get_previous_sibling (node); + } else { + e_editor_dom_selection_save (editor_page); + if (has_selection) { + WebKitDOMElement *selection_end_marker; + + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + node = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_end_marker)); + } + } + + if (!node || WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) + goto out; + + if (!WEBKIT_DOM_IS_TEXT (node)) { + if (webkit_dom_node_has_child_nodes (node)) + node = webkit_dom_node_get_first_child (node); + if (!WEBKIT_DOM_IS_TEXT (node)) + goto out; + } + + node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node)); + if (!(node_text && *node_text) || !g_utf8_validate (node_text, -1, NULL)) { + g_free (node_text); + goto out; + } + + if (strstr (node_text, "@") && !strstr (node_text, "://")) { + is_email_address = TRUE; + regex = g_regex_new (include_space ? E_MAIL_PATTERN_SPACE : E_MAIL_PATTERN, 0, 0, NULL); + } else + regex = g_regex_new (include_space ? URL_PATTERN_SPACE : URL_PATTERN, 0, 0, NULL); + + if (!regex) { + g_free (node_text); + goto out; + } + + g_regex_match_all (regex, node_text, G_REGEX_MATCH_NOTEMPTY, &match_info); + urls = g_match_info_fetch_all (match_info); + + if (urls) { + const gchar *end_of_match = NULL; + gchar *final_url, *url_end_raw, *url_text; + glong url_start, url_end, url_length; + WebKitDOMNode *url_text_node; + WebKitDOMElement *anchor; + + g_match_info_fetch_pos (match_info, 0, &start_pos_url, &end_pos_url); + + /* Get start and end position of url in node's text because positions + * that we get from g_match_info_fetch_pos are not UTF-8 aware */ + url_end_raw = g_strndup(node_text, end_pos_url); + url_end = g_utf8_strlen (url_end_raw, -1); + url_length = g_utf8_strlen (urls[0], -1); + + end_of_match = url_end_raw + end_pos_url - (include_space ? 3 : 2); + /* URLs are extremely unlikely to end with any punctuation, so + * strip any trailing punctuation off from link and put it after + * the link. Do the same for any closing double-quotes as well. */ + while (end_of_match && end_of_match != url_end_raw && strchr (URL_INVALID_TRAILING_CHARS, *end_of_match)) { + url_length--; + url_end--; + end_of_match--; + } + + url_start = url_end - url_length; + + webkit_dom_text_split_text ( + WEBKIT_DOM_TEXT (node), + include_space ? url_end - 1 : url_end, + NULL); + + webkit_dom_text_split_text ( + WEBKIT_DOM_TEXT (node), url_start, NULL); + url_text_node = webkit_dom_node_get_next_sibling (node); + url_text = webkit_dom_character_data_get_data ( + WEBKIT_DOM_CHARACTER_DATA (url_text_node)); + + if (g_str_has_prefix (url_text, "www.")) + final_url = g_strconcat ("http://" , url_text, NULL); + else if (is_email_address) + final_url = g_strconcat ("mailto:" , url_text, NULL); + else + final_url = g_strdup (url_text); + + /* Create and prepare new anchor element */ + anchor = webkit_dom_document_create_element (document, "A", NULL); + + webkit_dom_element_set_inner_html (anchor, url_text, NULL); + + webkit_dom_html_anchor_element_set_href ( + WEBKIT_DOM_HTML_ANCHOR_ELEMENT (anchor), + final_url); + + /* Insert new anchor element into document */ + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (anchor), + WEBKIT_DOM_NODE (url_text_node), + NULL); + + g_free (url_end_raw); + g_free (final_url); + g_free (url_text); + } else { + gboolean appending_to_link = FALSE; + gchar *href, *text, *url, *text_to_append = NULL; + gint diff; + WebKitDOMElement *parent; + WebKitDOMNode *prev_sibling; + + parent = webkit_dom_node_get_parent_element (node); + prev_sibling = webkit_dom_node_get_previous_sibling (node); + + /* If previous sibling is ANCHOR and actual text node is not beginning with + * space => we're appending to link */ + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) { + text_to_append = webkit_dom_node_get_text_content (node); + if (text_to_append && *text_to_append && + !strstr (text_to_append, " ") && + !(strchr (URL_INVALID_TRAILING_CHARS, *text_to_append) && + !(*text_to_append == '?' && strlen(text_to_append) > 1)) && + !g_str_has_prefix (text_to_append, UNICODE_NBSP)) { + + appending_to_link = TRUE; + parent = WEBKIT_DOM_ELEMENT (prev_sibling); + /* If the node(text) contains the some of unwanted characters + * split it into two nodes and select the right one. */ + if (g_str_has_suffix (text_to_append, UNICODE_NBSP) || + g_str_has_suffix (text_to_append, UNICODE_ZERO_WIDTH_SPACE)) { + webkit_dom_text_split_text ( + WEBKIT_DOM_TEXT (node), + g_utf8_strlen (text_to_append, -1) - 1, + NULL); + g_free (text_to_append); + text_to_append = webkit_dom_node_get_text_content (node); + } + } + } + + /* If parent is ANCHOR => we're editing the link */ + if ((!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) && !appending_to_link) || !text_to_append) { + g_match_info_free (match_info); + g_regex_unref (regex); + g_free (node_text); + g_free (text_to_append); + goto out; + } + + /* edit only if href and description are the same */ + href = webkit_dom_html_anchor_element_get_href ( + WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent)); + + if (appending_to_link) { + gchar *inner_text; + + inner_text = + webkit_dom_html_element_get_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (parent)), + + text = g_strconcat (inner_text, text_to_append, NULL); + g_free (inner_text); + } else + text = webkit_dom_html_element_get_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (parent)); + + element_remove_class (parent, "-x-evo-visited-link"); + + if (strstr (href, "://") && !strstr (text, "://")) { + url = strstr (href, "://") + 3; + diff = strlen (text) - strlen (url); + + if (text [strlen (text) - 1] != '/') + diff++; + + if ((g_strcmp0 (url, text) != 0 && ABS (diff) == 1) || appending_to_link) { + gchar *inner_html, *protocol, *new_href; + + protocol = g_strndup (href, strstr (href, "://") - href + 3); + inner_html = webkit_dom_element_get_inner_html (parent); + new_href = g_strconcat ( + protocol, inner_html, appending_to_link ? text_to_append : "", NULL); + + webkit_dom_html_anchor_element_set_href ( + WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent), + new_href); + + if (appending_to_link) { + webkit_dom_html_element_insert_adjacent_html ( + WEBKIT_DOM_HTML_ELEMENT (parent), + "beforeend", + text_to_append, + NULL); + + remove_node (node); + } + + g_free (new_href); + g_free (protocol); + g_free (inner_html); + } + } else { + diff = strlen (text) - strlen (href); + if (text [strlen (text) - 1] != '/') + diff++; + + if ((g_strcmp0 (href, text) != 0 && ABS (diff) == 1) || appending_to_link) { + gchar *inner_html; + gchar *new_href; + + inner_html = webkit_dom_element_get_inner_html (parent); + new_href = g_strconcat ( + inner_html, + appending_to_link ? text_to_append : "", + NULL); + + webkit_dom_html_anchor_element_set_href ( + WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent), + new_href); + + if (appending_to_link) { + webkit_dom_html_element_insert_adjacent_html ( + WEBKIT_DOM_HTML_ELEMENT (parent), + "beforeend", + text_to_append, + NULL); + + remove_node (node); + } + + g_free (new_href); + g_free (inner_html); + } + + } + g_free (text_to_append); + g_free (text); + g_free (href); + } + + g_match_info_free (match_info); + g_regex_unref (regex); + g_free (node_text); + + out: + if (!return_key_pressed) + e_editor_dom_selection_restore (editor_page); +} + +void +e_editor_dom_embed_style_sheet (EEditorPage *editor_page, + const gchar *style_sheet_content) +{ + WebKitDOMDocument *document; + WebKitDOMElement *sheet; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + e_dom_utils_create_and_add_css_style_sheet (document, "-x-evo-composer-sheet"); + + sheet = webkit_dom_document_get_element_by_id (document, "-x-evo-composer-sheet"); + webkit_dom_element_set_attribute ( + sheet, + "type", + "text/css", + NULL); + + webkit_dom_element_set_inner_html (sheet, style_sheet_content, NULL); +} + +void +e_editor_dom_remove_embedded_style_sheet (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *sheet; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + sheet = webkit_dom_document_get_element_by_id ( + document, "-x-evo-composer-sheet"); + + remove_node (WEBKIT_DOM_NODE (sheet)); +} + +static void +insert_delete_event (EEditorPage *editor_page, + WebKitDOMRange *range) +{ + EEditorHistoryEvent *ev; + WebKitDOMDocumentFragment *fragment; + EEditorUndoRedoManager *manager; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (e_editor_undo_redo_manager_is_operation_in_progress (manager)) + return; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_DELETE; + + fragment = webkit_dom_range_clone_contents (range, NULL); + ev->data.fragment = fragment; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->after.start.x = ev->before.start.x; + ev->after.start.y = ev->before.start.y; + ev->after.end.x = ev->before.start.x; + ev->after.end.y = ev->before.start.y; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_AND; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); +} + +/* Based on original use_pictograms() from GtkHTML */ +static const gchar *emoticons_chars = + /* 0 */ "DO)(|/PQ*!" + /* 10 */ "S\0:-\0:\0:-\0" + /* 20 */ ":\0:;=-\"\0:;" + /* 30 */ "B\"|\0:-'\0:X" + /* 40 */ "\0:\0:-\0:\0:-" + /* 50 */ "\0:\0:-\0:\0:-" + /* 60 */ "\0:\0:\0:-\0:\0" + /* 70 */ ":-\0:\0:-\0:\0"; +static gint emoticons_states[] = { + /* 0 */ 12, 17, 22, 34, 43, 48, 53, 58, 65, 70, + /* 10 */ 75, 0, -15, 15, 0, -15, 0, -17, 20, 0, + /* 20 */ -17, 0, -14, -20, -14, 28, 63, 0, -14, -20, + /* 30 */ -3, 63, -18, 0, -12, 38, 41, 0, -12, -2, + /* 40 */ 0, -4, 0, -10, 46, 0, -10, 0, -19, 51, + /* 50 */ 0, -19, 0, -11, 56, 0, -11, 0, -13, 61, + /* 60 */ 0, -13, 0, -6, 0, 68, -7, 0, -7, 0, + /* 70 */ -16, 73, 0, -16, 0, -21, 78, 0, -21, 0 }; +static const gchar *emoticons_icon_names[] = { + "face-angel", + "face-angry", + "face-cool", + "face-crying", + "face-devilish", + "face-embarrassed", + "face-kiss", + "face-laugh", /* not used */ + "face-monkey", /* not used */ + "face-plain", + "face-raspberry", + "face-sad", + "face-sick", + "face-smile", + "face-smile-big", + "face-smirk", + "face-surprise", + "face-tired", + "face-uncertain", + "face-wink", + "face-worried" +}; + +typedef struct _EmoticonLoadContext { + EEmoticon *emoticon; + EEditorPage *editor_page; + gchar *content_type; + gchar *name; +} EmoticonLoadContext; + +static EmoticonLoadContext * +emoticon_load_context_new (EEditorPage *editor_page, + EEmoticon *emoticon) +{ + EmoticonLoadContext *load_context; + + load_context = g_slice_new0 (EmoticonLoadContext); + load_context->emoticon = emoticon; + load_context->editor_page = editor_page; + + return load_context; +} + +static void +emoticon_load_context_free (EmoticonLoadContext *load_context) +{ + g_free (load_context->content_type); + g_free (load_context->name); + g_slice_free (EmoticonLoadContext, load_context); +} + +static void +emoticon_insert_span (EEmoticon *emoticon, + EmoticonLoadContext *load_context, + WebKitDOMElement *span) +{ + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + EEditorPage *editor_page = load_context->editor_page; + gboolean misplaced_selection = FALSE, smiley_written; + gchar *node_text = NULL; + const gchar *emoticon_start; + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *node, *insert_before, *prev_sibling, *next_sibling; + WebKitDOMNode *selection_end_marker_parent, *inserted_node; + WebKitDOMRange *range = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + smiley_written = e_editor_page_get_is_smiley_written (editor_page); + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (e_editor_dom_selection_is_collapsed (editor_page)) { + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + if (!smiley_written) { + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + if (e_editor_page_get_unicode_smileys_enabled (editor_page)) + ev->type = HISTORY_INPUT; + else { + ev->type = HISTORY_SMILEY; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + } + } + } + } else { + WebKitDOMRange *tmp_range = NULL; + + tmp_range = e_editor_dom_get_current_range (editor_page); + insert_delete_event (editor_page, tmp_range); + g_clear_object (&tmp_range); + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL); + + if (!smiley_written) { + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + + if (e_editor_page_get_unicode_smileys_enabled (editor_page)) + ev->type = HISTORY_INPUT; + else { + ev->type = HISTORY_SMILEY; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + } + } + } + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + } + + /* If the selection was not saved, move it into the first child of body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMHTMLElement *body; + WebKitDOMNode *child; + + body = webkit_dom_document_get_body (document); + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + dom_add_selection_markers_into_element_start ( + document, + WEBKIT_DOM_ELEMENT (child), + &selection_start_marker, + &selection_end_marker); + + if (ev && !e_editor_page_get_unicode_smileys_enabled (editor_page)) + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + } + + /* Sometimes selection end marker is in body. Move it into next sibling */ + selection_end_marker_parent = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_end_marker)); + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (selection_end_marker_parent)) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)), + WEBKIT_DOM_NODE (selection_end_marker), + WEBKIT_DOM_NODE (selection_start_marker), + NULL); + if (ev && !e_editor_page_get_unicode_smileys_enabled (editor_page)) + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + } + selection_end_marker_parent = webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_end_marker)); + + /* Determine before what node we have to insert the smiley */ + insert_before = WEBKIT_DOM_NODE (selection_start_marker); + prev_sibling = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (prev_sibling) { + if (webkit_dom_node_is_same_node ( + prev_sibling, WEBKIT_DOM_NODE (selection_end_marker))) { + insert_before = WEBKIT_DOM_NODE (selection_end_marker); + } else { + prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); + if (prev_sibling && + webkit_dom_node_is_same_node ( + prev_sibling, WEBKIT_DOM_NODE (selection_end_marker))) { + insert_before = WEBKIT_DOM_NODE (selection_end_marker); + } + } + } else + insert_before = WEBKIT_DOM_NODE (selection_start_marker); + + /* Look if selection is misplaced - that means that the selection was + * restored before the previously inserted smiley in situations when we + * are writing more smileys in a row */ + next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)); + if (next_sibling && WEBKIT_DOM_IS_ELEMENT (next_sibling)) + if (element_has_class (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-smiley-wrapper")) + misplaced_selection = TRUE; + + range = e_editor_dom_get_current_range (editor_page); + node = webkit_dom_range_get_end_container (range, NULL); + g_clear_object (&range); + if (WEBKIT_DOM_IS_TEXT (node)) + node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node)); + + if (misplaced_selection) { + /* Insert smiley and selection markers after it */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_next_sibling (next_sibling), + NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (selection_end_marker), + webkit_dom_node_get_next_sibling (next_sibling), + NULL); + if (e_editor_page_get_unicode_smileys_enabled (editor_page)) + inserted_node = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (span)), + webkit_dom_node_get_next_sibling (next_sibling), + NULL); + else + inserted_node = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (span), + webkit_dom_node_get_next_sibling (next_sibling), + NULL); + } else { + if (e_editor_page_get_unicode_smileys_enabled (editor_page)) + inserted_node = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (span)), + insert_before, + NULL); + else + inserted_node = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (span), + insert_before, + NULL); + } + + if (!e_editor_page_get_unicode_smileys_enabled (editor_page)) { + /* ​ == UNICODE_ZERO_WIDTH_SPACE */ + webkit_dom_html_element_insert_adjacent_html ( + WEBKIT_DOM_HTML_ELEMENT (span), "afterend", "​", NULL); + } + + if (ev) { + WebKitDOMDocumentFragment *fragment; + WebKitDOMNode *node; + + fragment = webkit_dom_document_create_document_fragment (document); + node = webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (inserted_node), TRUE, NULL), + NULL); + if (e_editor_page_get_unicode_smileys_enabled (editor_page)) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, TRUE)), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, FALSE)), + NULL); + } else + webkit_dom_html_element_insert_adjacent_html ( + WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "​", NULL); + ev->data.fragment = fragment; + } + + /* Remove the text that represents the text version of smiley that was + * written into the composer. */ + if (node_text && smiley_written) { + emoticon_start = g_utf8_strrchr ( + node_text, -1, g_utf8_get_char (emoticon->text_face)); + /* Check if the written smiley is really the one that we inserted. */ + if (emoticon_start) { + /* The written smiley is the same as text version. */ + if (g_str_has_prefix (emoticon_start, emoticon->text_face)) { + webkit_dom_character_data_delete_data ( + WEBKIT_DOM_CHARACTER_DATA (node), + g_utf8_strlen (node_text, -1) - strlen (emoticon_start), + strlen (emoticon->text_face), + NULL); + } else if (strstr (emoticon->text_face, "-")) { + gboolean same = TRUE, compensate = FALSE; + gint ii = 0, jj = 0; + + /* Try to recognize smileys without the dash e.g. :). */ + while (emoticon_start[ii] && emoticon->text_face[jj]) { + if (emoticon_start[ii] == emoticon->text_face[jj]) { + if (emoticon->text_face[jj+1] && emoticon->text_face[jj+1] == '-') { + ii++; + jj+=2; + compensate = TRUE; + } else { + ii++; + jj++; + } + } else { + same = FALSE; + break; + } + } + + if (same) { + webkit_dom_character_data_delete_data ( + WEBKIT_DOM_CHARACTER_DATA (node), + g_utf8_strlen (node_text, -1) - strlen (emoticon_start), + ii, + NULL); + } + /* If we recognize smiley without dash, but we inserted + * the text version with dash we need it insert new + * history input event with that dash. */ + if (compensate) + e_editor_undo_redo_manager_insert_dash_history_event (manager); + } + } + + e_editor_page_set_is_smiley_written (editor_page, FALSE); + } + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + + e_editor_page_emit_content_changed (editor_page); + + g_free (node_text); +} + +static void +emoticon_read_async_cb (GFile *file, + GAsyncResult *result, + EmoticonLoadContext *load_context) +{ + EEmoticon *emoticon = load_context->emoticon; + EEditorPage *editor_page = load_context->editor_page; + GError *error = NULL; + gboolean html_mode; + gchar *mime_type; + gchar *base64_encoded, *output, *data; + GFileInputStream *input_stream; + GOutputStream *output_stream; + gssize size; + WebKitDOMElement *wrapper, *image, *smiley_text; + WebKitDOMDocument *document; + + input_stream = g_file_read_finish (file, result, &error); + g_return_if_fail (!error && input_stream); + + output_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); + + size = g_output_stream_splice ( + output_stream, G_INPUT_STREAM (input_stream), + G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error); + + if (error || (size == -1)) + goto out; + + mime_type = g_content_type_get_mime_type (load_context->content_type); + + data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream)); + base64_encoded = g_base64_encode ((const guchar *) data, size); + output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL); + + html_mode = e_editor_page_get_html_mode (editor_page); + document = e_editor_page_get_document (editor_page); + + /* Insert span with image representation and another one with text + * representation and hide/show them dependant on active composer mode */ + wrapper = webkit_dom_document_create_element (document, "SPAN", NULL); + if (html_mode) + webkit_dom_element_set_attribute ( + wrapper, "class", "-x-evo-smiley-wrapper -x-evo-resizable-wrapper", NULL); + else + webkit_dom_element_set_attribute ( + wrapper, "class", "-x-evo-smiley-wrapper", NULL); + + image = webkit_dom_document_create_element (document, "IMG", NULL); + webkit_dom_element_set_attribute (image, "src", output, NULL); + webkit_dom_element_set_attribute (image, "data-inline", "", NULL); + webkit_dom_element_set_attribute (image, "data-name", load_context->name, NULL); + webkit_dom_element_set_attribute (image, "alt", emoticon->text_face, NULL); + webkit_dom_element_set_attribute (image, "class", "-x-evo-smiley-img", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (image), NULL); + + smiley_text = webkit_dom_document_create_element (document, "SPAN", NULL); + webkit_dom_element_set_attribute (smiley_text, "class", "-x-evo-smiley-text", NULL); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (smiley_text), emoticon->text_face, NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (smiley_text), NULL); + + emoticon_insert_span (emoticon, load_context, wrapper); + + g_free (base64_encoded); + g_free (output); + g_free (mime_type); + g_object_unref (output_stream); + out: + emoticon_load_context_free (load_context); +} + +static void +emoticon_query_info_async_cb (GFile *file, + GAsyncResult *result, + EmoticonLoadContext *load_context) +{ + GError *error = NULL; + GFileInfo *info; + + info = g_file_query_info_finish (file, result, &error); + g_return_if_fail (!error && info); + + load_context->content_type = g_strdup (g_file_info_get_content_type (info)); + load_context->name = g_strdup (g_file_info_get_name (info)); + + g_file_read_async ( + file, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback) emoticon_read_async_cb, load_context); + + g_object_unref (info); +} + +void +e_editor_dom_insert_smiley (EEditorPage *editor_page, + EEmoticon *emoticon) +{ + WebKitDOMDocument *document; + GFile *file; + gchar *filename_uri; + EmoticonLoadContext *load_context; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + if (e_editor_page_get_unicode_smileys_enabled (editor_page)) { + WebKitDOMElement *wrapper; + + wrapper = webkit_dom_document_create_element (document, "SPAN", NULL); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (wrapper), emoticon->unicode_character, NULL); + + load_context = emoticon_load_context_new (editor_page, emoticon); + emoticon_insert_span (emoticon, load_context, wrapper); + emoticon_load_context_free (load_context); + } else { + filename_uri = e_emoticon_get_uri (emoticon); + g_return_if_fail (filename_uri != NULL); + + load_context = emoticon_load_context_new (editor_page, emoticon); + + file = g_file_new_for_uri (filename_uri); + g_file_query_info_async ( + file, "standard::*", G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback) emoticon_query_info_async_cb, load_context); + + g_free (filename_uri); + g_object_unref (file); + } +} + +void +e_editor_dom_insert_smiley_by_name (EEditorPage *editor_page, + const gchar *name) +{ + const EEmoticon *emoticon; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + emoticon = e_emoticon_chooser_lookup_emoticon (name); + e_editor_page_set_is_smiley_written (editor_page, FALSE); + e_editor_dom_insert_smiley (editor_page, (EEmoticon *) emoticon); +} + +void +e_editor_dom_check_magic_smileys (EEditorPage *editor_page) +{ + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + gint pos, state, relative, start; + gchar *node_text; + gunichar uc; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!e_editor_page_get_magic_smileys_enabled (editor_page)) + return; + + range = e_editor_dom_get_current_range (editor_page); + node = webkit_dom_range_get_end_container (range, NULL); + if (!WEBKIT_DOM_IS_TEXT (node)) { + g_clear_object (&range); + return; + } + + node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node)); + if (node_text == NULL) { + g_clear_object (&range); + return; + } + + start = webkit_dom_range_get_end_offset (range, NULL) - 1; + pos = start; + state = 0; + while (pos >= 0) { + uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos)); + relative = 0; + while (emoticons_chars[state + relative]) { + if (emoticons_chars[state + relative] == uc) + break; + relative++; + } + state = emoticons_states[state + relative]; + /* 0 .. not found, -n .. found n-th */ + if (state <= 0) + break; + pos--; + } + + /* Special case needed to recognize angel and devilish. */ + if (pos > 0 && state == -14) { + uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1)); + if (uc == 'O') { + state = -1; + pos--; + } else if (uc == '>') { + state = -5; + pos--; + } + } + + if (state < 0) { + const EEmoticon *emoticon; + + if (pos > 0) { + uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1)); + if (!g_unichar_isspace (uc)) { + g_free (node_text); + g_clear_object (&range); + return; + } + } + + emoticon = e_emoticon_chooser_lookup_emoticon ( + emoticons_icon_names[-state - 1]); + e_editor_page_set_is_smiley_written (editor_page, TRUE); + e_editor_dom_insert_smiley (editor_page, (EEmoticon *) emoticon); + } + + g_clear_object (&range); + g_free (node_text); +} + +static void +dom_set_links_active (WebKitDOMDocument *document, + gboolean active) +{ + WebKitDOMElement *style; + + style = webkit_dom_document_get_element_by_id (document, "-x-evo-style-a"); + if (style) + remove_node (WEBKIT_DOM_NODE (style)); + + if (!active) { + WebKitDOMHTMLHeadElement *head; + head = webkit_dom_document_get_head (document); + + style = webkit_dom_document_create_element (document, "STYLE", NULL); + webkit_dom_element_set_id (style, "-x-evo-style-a"); + webkit_dom_element_set_attribute (style, "type", "text/css", NULL); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (style), "a { cursor: text; }", NULL); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style), NULL); + } +} + +static void +fix_paragraph_structure_after_pressing_enter_after_smiley (WebKitDOMDocument *document) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_query_selector ( + document, "span.-x-evo-smiley-wrapper > br", NULL); + + if (element) { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + webkit_dom_element_set_inner_html ( + webkit_dom_node_get_parent_element (parent), + UNICODE_ZERO_WIDTH_SPACE, + NULL); + } +} + +static gboolean +fix_paragraph_structure_after_pressing_enter (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNode *body; + WebKitDOMNodeList *list; + gboolean prev_is_heading = FALSE; + gint ii, length; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)); + + /* When pressing Enter on empty line in the list (or after heading elements) + * WebKit will end that list and inserts <div><br></div> so mark it for wrapping. */ + list = webkit_dom_document_query_selector_all ( + document, "body > div > br", NULL); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *prev_sibling; + WebKitDOMNode *node = webkit_dom_node_get_parent_node ( + webkit_dom_node_list_item (list, ii)); + + prev_sibling = webkit_dom_node_get_previous_sibling (node); + if (prev_sibling && WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (prev_sibling)) + prev_is_heading = TRUE; + + webkit_dom_node_replace_child ( + body, + WEBKIT_DOM_NODE (e_editor_dom_prepare_paragraph (editor_page, FALSE)), + node, + NULL); + + g_object_unref (node); + } + g_object_unref (list); + + return prev_is_heading; +} + +static gboolean +surround_text_with_paragraph_if_needed (EEditorPage *editor_page, + WebKitDOMNode *node) +{ + WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (node); + WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (node); + WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); + WebKitDOMElement *element; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + /* All text in composer has to be written in div elements, so if + * we are writing something straight to the body, surround it with + * paragraph */ + if (WEBKIT_DOM_IS_TEXT (node) && + (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent) || + WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent))) { + element = e_editor_dom_put_node_into_paragraph (editor_page, node, TRUE); + if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent)) + webkit_dom_element_remove_attribute (element, "style"); + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling)) + remove_node (next_sibling); + + /* Tab character */ + if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "Apple-tab-span")) { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (element), + prev_sibling, + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (element)), + NULL); + } + + return TRUE; + } + + return FALSE; +} + +static gboolean +selection_is_in_table (WebKitDOMDocument *document, + gboolean *first_cell, + WebKitDOMNode **table_node) +{ + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMNode *node, *parent; + WebKitDOMRange *range = NULL; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (first_cell != NULL) + *first_cell = FALSE; + + if (table_node != NULL) + *table_node = NULL; + + if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { + g_clear_object (&dom_selection); + return FALSE; + } + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + node = webkit_dom_range_get_start_container (range, NULL); + g_clear_object (&dom_selection); + + parent = node; + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent)) { + if (first_cell != NULL) { + if (!webkit_dom_node_get_previous_sibling (parent)) { + gboolean on_start = TRUE; + WebKitDOMNode *tmp; + + tmp = webkit_dom_node_get_previous_sibling (node); + if (!tmp && WEBKIT_DOM_IS_TEXT (node)) + on_start = webkit_dom_range_get_start_offset (range, NULL) == 0; + else if (tmp) + on_start = FALSE; + + if (on_start) { + node = webkit_dom_node_get_parent_node (parent); + if (node && WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (node)) + if (!webkit_dom_node_get_previous_sibling (node)) + *first_cell = TRUE; + } + } + } else { + g_clear_object (&range); + return TRUE; + } + } + if (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (parent)) { + if (table_node != NULL) + *table_node = parent; + else { + g_clear_object (&range); + return TRUE; + } + } + parent = webkit_dom_node_get_parent_node (parent); + } + + g_clear_object (&range); + + if (table_node == NULL) + return FALSE; + + return *table_node != NULL; +} + +static gboolean +jump_to_next_table_cell (WebKitDOMDocument *document, + gboolean jump_back) +{ + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMNode *node, *cell; + WebKitDOMRange *range = NULL; + + if (!selection_is_in_table (document, NULL, NULL)) + return FALSE; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + node = webkit_dom_range_get_start_container (range, NULL); + + cell = node; + while (cell && !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell)) { + cell = webkit_dom_node_get_parent_node (cell); + } + + if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell)) { + g_clear_object (&range); + g_clear_object (&dom_selection); + return FALSE; + } + + if (jump_back) { + /* Get previous cell */ + node = webkit_dom_node_get_previous_sibling (cell); + if (!node || !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) { + /* No cell, go one row up. */ + node = webkit_dom_node_get_parent_node (cell); + node = webkit_dom_node_get_previous_sibling (node); + if (node && WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (node)) { + node = webkit_dom_node_get_last_child (node); + } else { + /* No row above, move to the block before table. */ + node = webkit_dom_node_get_parent_node (cell); + while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node))) + node = webkit_dom_node_get_parent_node (node); + + node = webkit_dom_node_get_previous_sibling (node); + } + } + } else { + /* Get next cell */ + node = webkit_dom_node_get_next_sibling (cell); + if (!node || !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) { + /* No cell, go one row below. */ + node = webkit_dom_node_get_parent_node (cell); + node = webkit_dom_node_get_next_sibling (node); + if (node && WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (node)) { + node = webkit_dom_node_get_first_child (node); + } else { + /* No row below, move to the block after table. */ + node = webkit_dom_node_get_parent_node (cell); + while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node))) + node = webkit_dom_node_get_parent_node (node); + + node = webkit_dom_node_get_next_sibling (node); + } + } + } + + if (!node) { + g_clear_object (&range); + g_clear_object (&dom_selection); + return FALSE; + } + + webkit_dom_range_select_node_contents (range, node, NULL); + webkit_dom_range_collapse (range, TRUE, NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + g_clear_object (&dom_selection); + + return TRUE; +} + +static gboolean +save_history_before_event_in_table (EEditorPage *editor_page, + WebKitDOMRange *range) +{ + WebKitDOMNode *node; + WebKitDOMElement *block; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + node = webkit_dom_range_get_start_container (range, NULL); + if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) + block = WEBKIT_DOM_ELEMENT (node); + else + block = get_parent_block_element (node); + + if (block && WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (block)) { + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_TABLE_INPUT; + + if (block) { + e_editor_dom_selection_save (editor_page); + ev->data.dom.from = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (block), TRUE, NULL); + e_editor_dom_selection_restore (editor_page); + } else + ev->data.dom.from = NULL; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + + return TRUE; + } + + return FALSE; +} + +static gboolean +insert_tabulator (EEditorPage *editor_page) +{ + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + gboolean success; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INPUT; + + if (!e_editor_dom_selection_is_collapsed (editor_page)) { + WebKitDOMRange *tmp_range = NULL; + + tmp_range = e_editor_dom_get_current_range (editor_page); + insert_delete_event (editor_page, tmp_range); + g_clear_object (&tmp_range); + } + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->before.end.x = ev->before.start.x; + ev->before.end.y = ev->before.start.y; + } + + success = e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, "\t"); + + if (ev) { + if (success) { + WebKitDOMDocument *document; + WebKitDOMElement *element; + WebKitDOMDocumentFragment *fragment; + + document = e_editor_page_get_document (editor_page); + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + fragment = webkit_dom_document_create_document_fragment (document); + element = webkit_dom_document_create_element (document, "span", NULL); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (element), "\t", NULL); + webkit_dom_element_set_attribute ( + element, "class", "Apple-tab-span", NULL); + webkit_dom_element_set_attribute ( + element, "style", "white-space:pre", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), WEBKIT_DOM_NODE (element), NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE (dom_create_selection_marker (document, TRUE)), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE (dom_create_selection_marker (document, FALSE)), + NULL); + ev->data.fragment = fragment; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + e_editor_page_emit_content_changed (editor_page); + } else { + e_editor_undo_redo_manager_remove_current_history_event (manager); + e_editor_undo_redo_manager_remove_current_history_event (manager); + g_free (ev); + } + } + + return success; +} + +static void +body_keypress_event_cb (WebKitDOMElement *element, + WebKitDOMUIEvent *event, + EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + if (!webkit_dom_range_get_collapsed (range, NULL)) + insert_delete_event (editor_page, range); + + g_clear_object (&dom_selection); + g_clear_object (&range); +} + +void +e_editor_dom_set_monospace_font_family_on_body (WebKitDOMElement *body, + gboolean html_mode) +{ + /* If copying some content in view, WebKit adds various information about + * the content's style (such as color, font size, ..) to the resulting HTML + * to correctly apply the style when pasting the content later. The thing + * is that in plain text mode the only font allowed is the monospaced one, + * but we are forcing it through user style sheet in WebKitWebSettings and + * sadly WebKit doesn't count with it, so when the content is pasted, + * WebKit wraps it inside SPANs and sets the font-family style on them. + * The problem is that when we switch to the HTML mode, the pasted content + * will have the monospaced font set. To avoid it we need to set the + * font-family style to the body, so WebKit will know about it and will + * avoid the described behaviour. */ + /* When we are deleting a content from the PRE elements we need to turn + * this off, otherwise we will end with the same unwanted behavior (the + * text between the caret and the end of the element will be wrapped + * inside a SPAN element. */ + if (!html_mode) { + element_rename_attribute (WEBKIT_DOM_ELEMENT (body), "data-style", "style"); + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body), + "style", + "font-family: Monospace;", + NULL); + } else { + element_rename_attribute (WEBKIT_DOM_ELEMENT (body), "style", "data-style"); + } +} + +static void +body_keydown_event_cb (WebKitDOMElement *element, + WebKitDOMUIEvent *event, + EEditorPage *editor_page) +{ + gboolean backspace_key, delete_key, space_key, return_key; + gboolean shift_key, control_key; + glong key_code; + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); + + key_code = webkit_dom_ui_event_get_key_code (event); + delete_key = key_code == HTML_KEY_CODE_DELETE; + return_key = key_code == HTML_KEY_CODE_RETURN; + backspace_key = key_code == HTML_KEY_CODE_BACKSPACE; + space_key = key_code == HTML_KEY_CODE_SPACE; + + if (key_code == HTML_KEY_CODE_CONTROL) { + dom_set_links_active (document, TRUE); + return; + } + + e_editor_page_set_dont_save_history_in_body_input (editor_page, delete_key || backspace_key); + + e_editor_page_set_return_key_pressed (editor_page, return_key); + e_editor_page_set_space_key_pressed (editor_page, space_key); + + if (!(delete_key || return_key || backspace_key || space_key)) + return; + + shift_key = webkit_dom_keyboard_event_get_shift_key (WEBKIT_DOM_KEYBOARD_EVENT (event)); + control_key = webkit_dom_keyboard_event_get_ctrl_key (WEBKIT_DOM_KEYBOARD_EVENT (event)); + + if (key_code == HTML_KEY_CODE_TABULATOR) { + if (jump_to_next_table_cell (document, shift_key)) { + webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event)); + goto out; + } + + if (!shift_key && insert_tabulator (editor_page)) + webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event)); + + goto out; + } + + if (return_key && e_editor_dom_key_press_event_process_return_key (editor_page)) { + webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event)); + goto out; + } + + if (backspace_key && e_editor_dom_key_press_event_process_backspace_key (editor_page)) { + webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event)); + goto out; + } + + if (delete_key || backspace_key) { + if (e_editor_dom_key_press_event_process_delete_or_backspace_key (editor_page, key_code, control_key, delete_key)) + webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event)); + else if (!e_editor_page_get_html_mode (editor_page)) + e_editor_dom_set_monospace_font_family_on_body (element, TRUE); + goto out; + } + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + if (save_history_before_event_in_table (editor_page, range)) + goto out; + + if (return_key) { + EEditorHistoryEvent *ev; + EEditorUndoRedoManager *manager; + + /* Insert new history event for Return to have the right coordinates. + * The fragment will be added later. */ + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INPUT; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + out: + g_clear_object (&range); + g_clear_object (&dom_selection); +} + +static gboolean +save_history_after_event_in_table (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMElement *element; + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + EEditorHistoryEvent *ev; + EEditorUndoRedoManager *manager; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (!webkit_dom_dom_selection_get_range_count (dom_selection)) { + g_clear_object (&dom_selection); + return FALSE; + } + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + /* Find if writing into table. */ + node = webkit_dom_range_get_start_container (range, NULL); + if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) + element = WEBKIT_DOM_ELEMENT (node); + else + element = get_parent_block_element (node); + + g_clear_object (&dom_selection); + g_clear_object (&range); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + /* If writing to table we have to create different history event. */ + if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (element)) { + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + if (ev->type != HISTORY_TABLE_INPUT) + return FALSE; + } else + return FALSE; + + e_editor_dom_selection_save (editor_page); + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + ev->data.dom.to = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element), TRUE, NULL); + + e_editor_dom_selection_restore (editor_page); + + return TRUE; +} + +static void +save_history_for_input (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL, *range_clone = NULL; + WebKitDOMNode *start_container; + EEditorHistoryEvent *ev; + EEditorUndoRedoManager *manager; + glong offset; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + manager = e_editor_page_get_undo_redo_manager (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (!webkit_dom_dom_selection_get_range_count (dom_selection)) { + g_clear_object (&dom_selection); + return; + } + + if (e_editor_page_get_return_key_pressed (editor_page)) { + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + if (ev->type != HISTORY_INPUT) { + g_clear_object (&dom_selection); + return; + } + } else { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INPUT; + } + + e_editor_page_block_selection_changed (editor_page); + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + range_clone = webkit_dom_range_clone_range (range, NULL); + offset = webkit_dom_range_get_start_offset (range_clone, NULL); + start_container = webkit_dom_range_get_start_container (range_clone, NULL); + if (offset > 0) + webkit_dom_range_set_start ( + range_clone, + start_container, + offset - 1, + NULL); + fragment = webkit_dom_range_clone_contents (range_clone, NULL); + /* We have to specially handle Return key press */ + if (e_editor_page_get_return_key_pressed (editor_page)) { + WebKitDOMElement *element_start, *element_end; + WebKitDOMNode *parent_start, *parent_end, *node; + + element_start = webkit_dom_document_create_element (document, "span", NULL); + webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element_start), NULL); + webkit_dom_dom_selection_modify (dom_selection, "move", "left", "character"); + g_clear_object (&range); + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + element_end = webkit_dom_document_create_element (document, "span", NULL); + webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element_end), NULL); + + parent_start = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element_start)); + parent_end = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element_end)); + + while (parent_start && parent_end && !webkit_dom_node_is_same_node (parent_start, parent_end)) { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (parent_start, FALSE, NULL), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), + NULL); + parent_start = webkit_dom_node_get_parent_node (parent_start); + parent_end = webkit_dom_node_get_parent_node (parent_end); + } + + node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + while (webkit_dom_node_get_next_sibling (node)) { + WebKitDOMNode *last_child; + + last_child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)); + webkit_dom_node_append_child ( + webkit_dom_node_get_previous_sibling (last_child), + last_child, + NULL); + } + + node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)); + while (webkit_dom_node_get_last_child (node)) { + node = webkit_dom_node_get_last_child (node); + } + + webkit_dom_node_append_child ( + node, + WEBKIT_DOM_NODE ( + webkit_dom_document_create_element (document, "br", NULL)), + NULL); + webkit_dom_node_append_child ( + node, + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, TRUE)), + NULL); + webkit_dom_node_append_child ( + node, + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, FALSE)), + NULL); + + remove_node (WEBKIT_DOM_NODE (element_start)); + remove_node (WEBKIT_DOM_NODE (element_end)); + + g_object_set_data ( + G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1)); + + webkit_dom_dom_selection_modify (dom_selection, "move", "right", "character"); + } else { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, TRUE)), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, FALSE)), + NULL); + } + + g_clear_object (&dom_selection); + g_clear_object (&range); + g_clear_object (&range_clone); + + e_editor_page_unblock_selection_changed (editor_page); + + ev->data.fragment = fragment; + + if (!e_editor_page_get_return_key_pressed (editor_page)) + e_editor_undo_redo_manager_insert_history_event (manager, ev); +} + +typedef struct _TimeoutContext TimeoutContext; + +struct _TimeoutContext { + EEditorPage *editor_page; +}; + +static void +timeout_context_free (TimeoutContext *context) +{ + g_slice_free (TimeoutContext, context); +} + +static gboolean +force_spell_check_on_timeout (TimeoutContext *context) +{ + e_editor_dom_force_spell_check_in_viewport (context->editor_page); + e_editor_page_set_spell_check_on_scroll_event_source_id (context->editor_page, 0); + return FALSE; +} + +static void +body_scroll_event_cb (WebKitDOMElement *element, + WebKitDOMEvent *event, + EEditorPage *editor_page) +{ + TimeoutContext *context; + guint id; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!e_editor_page_get_inline_spelling_enabled (editor_page)) + return; + + context = g_slice_new0 (TimeoutContext); + context->editor_page = editor_page; + + id = e_editor_page_get_spell_check_on_scroll_event_source_id (editor_page); + if (id > 0) + g_source_remove (id); + + id = g_timeout_add_seconds_full ( + 1, + G_PRIORITY_DEFAULT, + (GSourceFunc)force_spell_check_on_timeout, + context, + (GDestroyNotify)timeout_context_free); + + e_editor_page_set_spell_check_on_scroll_event_source_id (editor_page, id); +} + +void +e_editor_dom_body_input_event_process (EEditorPage *editor_page, + WebKitDOMEvent *event) +{ + WebKitDOMDocument *document; + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + EEditorUndoRedoManager *manager; + gboolean do_spell_check = FALSE; + gboolean html_mode; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + range = e_editor_dom_get_current_range (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + html_mode = e_editor_page_get_html_mode (editor_page); + e_editor_page_emit_content_changed (editor_page); + + if (e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + e_editor_undo_redo_manager_set_operation_in_progress (manager, FALSE); + e_editor_page_set_dont_save_history_in_body_input (editor_page, FALSE); + do_spell_check = TRUE; + goto out; + } + + /* When the Backspace is pressed in a bulleted list item with just one + * character left in it, WebKit will create another BR element in the + * item. */ + if (!html_mode) { + WebKitDOMElement *element; + + element = webkit_dom_document_query_selector ( + document, "ul > li > br + br", NULL); + + if (element) + remove_node (WEBKIT_DOM_NODE (element)); + } + + if (!save_history_after_event_in_table (editor_page)) { + if (!e_editor_page_get_dont_save_history_in_body_input (editor_page)) + save_history_for_input (editor_page); + else + do_spell_check = TRUE; + } + + /* Don't try to look for smileys if we are deleting text. */ + if (!e_editor_page_get_dont_save_history_in_body_input (editor_page)) + e_editor_dom_check_magic_smileys (editor_page); + + e_editor_page_set_dont_save_history_in_body_input (editor_page, FALSE); + + if (e_editor_page_get_return_key_pressed (editor_page) || + e_editor_page_get_space_key_pressed (editor_page)) { + e_editor_dom_check_magic_links (editor_page, FALSE); + if (e_editor_page_get_return_key_pressed (editor_page)) { + if (fix_paragraph_structure_after_pressing_enter (editor_page) && + html_mode) { + /* When the return is pressed in a H1-6 element, WebKit doesn't + * continue with the same element, but creates normal paragraph, + * so we have to unset the bold font. */ + e_editor_undo_redo_manager_set_operation_in_progress (manager, TRUE); + e_editor_dom_selection_set_bold (editor_page, FALSE); + e_editor_undo_redo_manager_set_operation_in_progress (manager, FALSE); + } + + fix_paragraph_structure_after_pressing_enter_after_smiley (document); + + do_spell_check = TRUE; + } + } else { + WebKitDOMNode *node; + + node = webkit_dom_range_get_end_container (range, NULL); + + if (surround_text_with_paragraph_if_needed (editor_page, node)) { + WebKitDOMElement *element; + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + e_editor_dom_selection_restore (editor_page); + } + + if (WEBKIT_DOM_IS_TEXT (node)) { + gchar *text; + + text = webkit_dom_node_get_text_content (node); + + if (text && *text && *text != ' ' && !g_str_has_prefix (text, UNICODE_NBSP)) { + gboolean valid = FALSE; + + if (*text == '?' && strlen (text) > 1) + valid = TRUE; + else if (!strchr (URL_INVALID_TRAILING_CHARS, *text)) + valid = TRUE; + + if (valid) { + WebKitDOMNode *prev_sibling; + + prev_sibling = webkit_dom_node_get_previous_sibling (node); + + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) + e_editor_dom_check_magic_links (editor_page, FALSE); + } + } + g_free (text); + } + } + + node = webkit_dom_range_get_end_container (range, NULL); + + /* After toggling monospaced format, we are using UNICODE_ZERO_WIDTH_SPACE + * to move caret into right space. When this callback is called it is not + * necessary anymore so remove it */ + if (html_mode) { + WebKitDOMElement *parent = webkit_dom_node_get_parent_element (node); + + if (parent) { + WebKitDOMNode *prev_sibling; + + prev_sibling = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (parent)); + + if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) { + gchar *text = webkit_dom_node_get_text_content ( + prev_sibling); + + if (g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0) + remove_node (prev_sibling); + + g_free (text); + } + + } + } + + /* If text before caret includes UNICODE_ZERO_WIDTH_SPACE character, remove it */ + if (WEBKIT_DOM_IS_TEXT (node)) { + gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node)); + glong length = webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)); + WebKitDOMNode *parent; + + /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE + * character as when we will remove it it will collapse */ + if (length > 1) { + if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE)) + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL); + else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE)) + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (node), length - 1, 1, "", NULL); + } + g_free (text); + + parent = webkit_dom_node_get_parent_node (node); + if (WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent) && + !webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-evo-paragraph")) { + if (html_mode) + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (parent), + "data-evo-paragraph", + "", + NULL); + else + e_editor_dom_set_paragraph_style ( + editor_page, WEBKIT_DOM_ELEMENT (parent), -1, 0, NULL); + } + + /* When new smiley is added we have to use UNICODE_HIDDEN_SPACE to set the + * caret position to right place. It is removed when user starts typing. But + * when the user will press left arrow he will move the caret into + * smiley wrapper. If he will start to write there we have to move the written + * text out of the wrapper and move caret to right place */ + if (WEBKIT_DOM_IS_ELEMENT (parent) && + element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text")) { + gchar *text; + WebKitDOMCharacterData *data; + WebKitDOMText *text_node; + + /* Split out the newly written character to its own text node, */ + data = WEBKIT_DOM_CHARACTER_DATA (node); + parent = webkit_dom_node_get_parent_node (parent); + text = webkit_dom_character_data_substring_data ( + data, + webkit_dom_character_data_get_length (data) - 1, + 1, + NULL); + webkit_dom_character_data_delete_data ( + data, + webkit_dom_character_data_get_length (data) - 1, + 1, + NULL); + text_node = webkit_dom_document_create_text_node (document, text); + g_free (text); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, FALSE)), + webkit_dom_node_get_next_sibling (parent), + NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, TRUE)), + webkit_dom_node_get_next_sibling (parent), + NULL); + /* Move the text node outside of smiley. */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + WEBKIT_DOM_NODE (text_node), + webkit_dom_node_get_next_sibling (parent), + NULL); + e_editor_dom_selection_restore (editor_page); + } + } + + /* Writing into quoted content */ + if (html_mode) { + gint citation_level; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *node, *parent; + + node = webkit_dom_range_get_end_container (range, NULL); + + citation_level = e_editor_dom_get_citation_level (node, FALSE); + if (citation_level == 0) + goto out; + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + if (selection_start_marker) + goto out; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + /* If the selection was not saved, move it into the first child of body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMHTMLElement *body; + WebKitDOMNode *child; + + body = webkit_dom_document_get_body (document); + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + dom_add_selection_markers_into_element_start ( + document, + WEBKIT_DOM_ELEMENT (child), + &selection_start_marker, + &selection_end_marker); + } + + /* We have to process elements only inside normal block */ + parent = WEBKIT_DOM_NODE (get_parent_block_element ( + WEBKIT_DOM_NODE (selection_start_marker))); + if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent)) { + e_editor_dom_selection_restore (editor_page); + goto out; + } + + if (selection_start_marker) { + gchar *content; + gint text_length, word_wrap_length, length; + WebKitDOMElement *block; + gboolean remove_quoting = FALSE; + + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + length = word_wrap_length - 2 * citation_level; + + block = WEBKIT_DOM_ELEMENT (parent); + if (webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block), ".-x-evo-quoted", NULL)) { + WebKitDOMNode *prev_sibling; + + prev_sibling = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_end_marker)); + + if (WEBKIT_DOM_IS_ELEMENT (prev_sibling)) + remove_quoting = element_has_class ( + WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted"); + } + + content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (block)); + text_length = g_utf8_strlen (content, -1); + g_free (content); + + /* Wrap and quote the line */ + if (!remove_quoting && text_length >= word_wrap_length) { + e_editor_dom_remove_quoting_from_element (block); + + block = e_editor_dom_wrap_paragraph_length (editor_page, block, length); + webkit_dom_node_normalize (WEBKIT_DOM_NODE (block)); + e_editor_dom_quote_plain_text_element_after_wrapping ( + editor_page, WEBKIT_DOM_ELEMENT (block), citation_level); + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + if (!selection_start_marker) + dom_add_selection_markers_into_element_end ( + document, + WEBKIT_DOM_ELEMENT (block), + NULL, + NULL); + + e_editor_dom_selection_restore (editor_page); + do_spell_check = TRUE; + goto out; + } + } + e_editor_dom_selection_restore (editor_page); + } + out: + if (do_spell_check) + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + + g_clear_object (&range); +} + +static void +body_input_event_cb (WebKitDOMElement *element, + WebKitDOMEvent *event, + EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + e_editor_dom_body_input_event_process (editor_page, event); +} + +void +e_editor_dom_remove_input_event_listener_from_body (EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!e_editor_page_get_body_input_event_removed (editor_page)) { + WebKitDOMDocument *document; + + document = e_editor_page_get_document (editor_page); + + webkit_dom_event_target_remove_event_listener ( + WEBKIT_DOM_EVENT_TARGET (webkit_dom_document_get_body (document)), + "input", + G_CALLBACK (body_input_event_cb), + FALSE); + + e_editor_page_set_body_input_event_removed (editor_page, TRUE); + } +} + +void +e_editor_dom_register_input_event_listener_on_body (EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_page_get_body_input_event_removed (editor_page)) { + WebKitDOMDocument *document; + + document = e_editor_page_get_document (editor_page); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (webkit_dom_document_get_body (document)), + "input", + G_CALLBACK (body_input_event_cb), + FALSE, + editor_page); + + e_editor_page_set_body_input_event_removed (editor_page, FALSE); + } +} + +static void +remove_empty_blocks (WebKitDOMDocument *document) +{ + gint ii, length; + WebKitDOMNodeList *list = NULL; + + list = webkit_dom_document_query_selector_all ( + document, "blockquote[type=cite] > :empty", NULL); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); + + list = webkit_dom_document_query_selector_all ( + document, "blockquote[type=cite]:empty", NULL); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); +} + +/* Following two functions are used when deleting the selection inside + * the quoted content. The thing is that normally the quote marks are not + * selectable by user. But this caused a lof of problems for WebKit when removing + * the selection. This will avoid it as when the delete or backspace key is pressed + * we will make the quote marks user selectable so they will act as any other text. + * On HTML keyup event callback we will make them again non-selectable. */ +void +e_editor_dom_disable_quote_marks_select (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLHeadElement *head; + WebKitDOMElement *style_element; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + head = webkit_dom_document_get_head (document); + + if (!webkit_dom_document_get_element_by_id (document, "-x-evo-quote-style")) { + style_element = webkit_dom_document_create_element (document, "style", NULL); + webkit_dom_element_set_id (style_element, "-x-evo-quote-style"); + webkit_dom_element_set_attribute (style_element, "type", "text/css", NULL); + webkit_dom_element_set_inner_html ( + style_element, + ".-x-evo-quoted { -webkit-user-select: none; }", + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style_element), NULL); + } +} + +static void +enable_quote_marks_select (WebKitDOMDocument *document) +{ + WebKitDOMElement *style_element; + + if ((style_element = webkit_dom_document_get_element_by_id (document, "-x-evo-quote-style"))) + remove_node (WEBKIT_DOM_NODE (style_element)); +} + +void +e_editor_dom_remove_node_and_parents_if_empty (WebKitDOMNode *node) +{ + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (node)); + + remove_node (WEBKIT_DOM_NODE (node)); + + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + WebKitDOMNode *tmp; + + tmp = webkit_dom_node_get_parent_node (parent); + remove_node_if_empty (parent); + parent = tmp; + } +} + +void +e_editor_dom_merge_siblings_if_necessary (EEditorPage *editor_page, + WebKitDOMDocumentFragment *deleted_content) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element, *prev_element; + WebKitDOMNode *child; + WebKitDOMNodeList *list = NULL; + gboolean equal_nodes; + gint ii, length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + if ((element = webkit_dom_document_get_element_by_id (document, "-x-evo-main-cite"))) + webkit_dom_element_remove_attribute (element, "id"); + + element = webkit_dom_document_query_selector (document, "blockquote:not([data-evo-query-skip]) + blockquote", NULL); + if (!element) + goto signature; + repeat: + child = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + if (WEBKIT_DOM_IS_ELEMENT (child)) + prev_element = WEBKIT_DOM_ELEMENT (child); + else + goto signature; + + equal_nodes = webkit_dom_node_is_equal_node ( + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element), FALSE, NULL), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (prev_element), FALSE, NULL)); + + if (equal_nodes) { + if (webkit_dom_element_get_child_element_count (element) > + webkit_dom_element_get_child_element_count (prev_element)) { + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (prev_element), child, NULL); + remove_node (WEBKIT_DOM_NODE (element)); + } else { + while ((child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (prev_element)))) + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (element), + child, + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (element)), + NULL); + remove_node (WEBKIT_DOM_NODE (prev_element)); + } + } else + webkit_dom_element_set_attribute (element, "data-evo-query-skip", "", NULL); + + element = webkit_dom_document_query_selector (document, "blockquote:not([data-evo-query-skip]) + blockquote", NULL); + if (element) + goto repeat; + + signature: + list = webkit_dom_document_query_selector_all ( + document, "blockquote[data-evo-query-skip]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (node), "data-evo-query-skip"); + g_object_unref (node); + } + g_clear_object (&list); + + if (!deleted_content) + return; + + /* Replace the corrupted signatures with the right one. */ + element = webkit_dom_document_query_selector ( + document, ".-x-evo-signature-wrapper + .-x-evo-signature-wrapper", NULL); + if (element) { + WebKitDOMElement *right_signature; + + right_signature = webkit_dom_document_fragment_query_selector ( + deleted_content, ".-x-evo-signature-wrapper", NULL); + remove_node (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element))); + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (right_signature), TRUE, NULL), + WEBKIT_DOM_NODE (element), + NULL); + } +} + +/* This will fix the structure after the situations where some text + * inside the quoted content is selected and afterwards deleted with + * BackSpace or Delete. */ +void +e_editor_dom_body_key_up_event_process_backspace_or_delete (EEditorPage *editor_page, + gboolean delete) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *parent, *node; + gint level; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_page_get_html_mode (editor_page)) + return; + + document = e_editor_page_get_document (editor_page); + e_editor_dom_disable_quote_marks_select (editor_page); + /* Remove empty blocks if presented. */ + remove_empty_blocks (document); + + e_editor_dom_selection_save (editor_page); + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + /* If we deleted a selection the caret will be inside the quote marks, fix it. */ + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); + if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quote-character")) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + webkit_dom_node_get_parent_node (parent)), + WEBKIT_DOM_NODE (selection_end_marker), + webkit_dom_node_get_next_sibling ( + webkit_dom_node_get_parent_node (parent)), + NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + webkit_dom_node_get_parent_node (parent)), + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_next_sibling ( + webkit_dom_node_get_parent_node (parent)), + NULL); + } + + /* Under some circumstances we will end with block inside the citation + * that has the quote marks removed and we have to reinsert them back. */ + level = e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (selection_start_marker), FALSE); + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)); + if (level > 0 && node && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) { + WebKitDOMElement *block; + + block = WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker))); + + e_editor_dom_remove_quoting_from_element (block); + if (webkit_dom_element_has_attribute (block, "data-evo-paragraph")) { + gint length, word_wrap_length; + + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + length = word_wrap_length - 2 * level; + block = e_editor_dom_wrap_paragraph_length (editor_page, block, length); + webkit_dom_node_normalize (WEBKIT_DOM_NODE (block)); + } + e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, block, level); + } else if (level > 0 && !node) { + WebKitDOMNode *prev_sibling; + + prev_sibling = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted") && + !webkit_dom_node_get_previous_sibling (prev_sibling)) + webkit_dom_node_append_child ( + parent, + WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), + NULL); + } + + e_editor_dom_merge_siblings_if_necessary (editor_page, NULL); + + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); +} + +void +e_editor_dom_body_key_up_event_process_return_key (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *parent; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + /* If the return is pressed in an unordered list in plain text mode + * the caret is moved to the "*" character before the newly inserted + * item. It looks like it is not enough that the item has BR element + * inside, but we have to again use the zero width space character + * to fix the situation. */ + if (e_editor_page_get_html_mode (editor_page)) + return; + + /* FIXME WK2 this is called twice */ + /* e_editor_dom_selection_save (editor_page); */ + + document = e_editor_page_get_document (editor_page); + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); + if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent) || + !WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (webkit_dom_node_get_parent_node (parent))) { + e_editor_dom_selection_restore (editor_page); + return; + } + + if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)) && + (!webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)) || + WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker))))) + webkit_dom_html_element_insert_adjacent_text ( + WEBKIT_DOM_HTML_ELEMENT (parent), + "afterbegin", + UNICODE_ZERO_WIDTH_SPACE, + NULL); + + e_editor_dom_selection_restore (editor_page); +} + +static void +body_keyup_event_cb (WebKitDOMElement *element, + WebKitDOMUIEvent *event, + EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + glong key_code; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); + if (!e_editor_page_is_composition_in_progress (editor_page)) + e_editor_dom_register_input_event_listener_on_body (editor_page); + + if (!e_editor_dom_selection_is_collapsed (editor_page)) + return; + + key_code = webkit_dom_ui_event_get_key_code (event); + if (key_code == HTML_KEY_CODE_BACKSPACE || key_code == HTML_KEY_CODE_DELETE) { + if (!e_editor_page_get_html_mode (editor_page)) { + WebKitDOMHTMLElement *body; + + body = webkit_dom_document_get_body (document); + + e_editor_dom_set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), FALSE); + } + e_editor_dom_body_key_up_event_process_backspace_or_delete (editor_page, key_code == HTML_KEY_CODE_DELETE); + + /* The content was wrapped and the coordinates + * of caret could be changed, so renew them. But + * only do that when we are not redoing a history + * event, otherwise it would modify the history. */ + if (e_editor_page_get_renew_history_after_coordinates (editor_page)) { + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + ev = e_editor_undo_redo_manager_get_current_history_event (manager); + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + } + } else if (key_code == HTML_KEY_CODE_CONTROL) + dom_set_links_active (document, FALSE); + else if (key_code == HTML_KEY_CODE_RETURN) + e_editor_dom_body_key_up_event_process_return_key (editor_page); +} + +static void +fix_structure_after_pasting_multiline_content (WebKitDOMNode *node) +{ + WebKitDOMNode *first_child, *parent; + + /* When pasting content that does not contain just the + * one line text WebKit inserts all the content after the + * first line into one element. So we have to take it out + * of this element and insert it after that element. */ + parent = webkit_dom_node_get_parent_node (node); + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) + return; + first_child = webkit_dom_node_get_first_child (parent); + while (first_child) { + WebKitDOMNode *next_child = + webkit_dom_node_get_next_sibling (first_child); + if (webkit_dom_node_has_child_nodes (first_child)) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + first_child, + parent, + NULL); + first_child = next_child; + } +} + +static gboolean +delete_hidden_space (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker, *block; + gint citation_level; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + if (!selection_start_marker || !selection_end_marker) + return FALSE; + + block = WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker))); + + citation_level = e_editor_dom_get_citation_level ( + WEBKIT_DOM_NODE (selection_start_marker), FALSE); + + if (selection_start_marker && citation_level > 0) { + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + WebKitDOMNode *node; + WebKitDOMDocumentFragment *fragment; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)); + if (!(WEBKIT_DOM_IS_ELEMENT (node) && + element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-quoted"))) + return FALSE; + + node = webkit_dom_node_get_previous_sibling (node); + if (!(WEBKIT_DOM_IS_ELEMENT (node) && + element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br"))) + return FALSE; + + node = webkit_dom_node_get_previous_sibling (node); + if (!(WEBKIT_DOM_IS_ELEMENT (node) && + webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), "data-hidden-space"))) + return FALSE; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_DELETE; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + + remove_node (node); + + e_editor_dom_wrap_and_quote_element (editor_page, block); + + fragment = webkit_dom_document_create_document_fragment (document); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + webkit_dom_document_create_text_node (document, " ")), + NULL); + ev->data.fragment = fragment; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + + return TRUE; + } + + return FALSE; +} + +gboolean +e_editor_dom_move_quoted_block_level_up (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *block; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + gboolean html_mode; + gint citation_level, success = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + manager = e_editor_page_get_undo_redo_manager (editor_page); + html_mode = e_editor_page_get_html_mode (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + if (!selection_start_marker || !selection_end_marker) + return FALSE; + + block = e_editor_dom_get_parent_block_node_from_child (WEBKIT_DOM_NODE (selection_start_marker)); + + citation_level = e_editor_dom_get_citation_level ( + WEBKIT_DOM_NODE (selection_start_marker), FALSE); + + if (selection_start_marker && citation_level > 0) { + if (webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block), ".-x-evo-quoted", NULL)) { + + WebKitDOMNode *prev_sibling; + + webkit_dom_node_normalize (block); + + prev_sibling = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)); + + if (!prev_sibling) { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) + prev_sibling = webkit_dom_node_get_previous_sibling (parent); + } + + if (WEBKIT_DOM_IS_ELEMENT (prev_sibling)) + success = element_has_class ( + WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted"); + + /* We really have to be in the beginning of paragraph and + * not on the beginning of some line in the paragraph */ + if (success && webkit_dom_node_get_previous_sibling (prev_sibling)) + success = FALSE; + } + + if (html_mode) + success = WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT ( + webkit_dom_node_get_parent_element (block)); + } + + if (!success) + return FALSE; + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_UNQUOTE; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + ev->data.dom.from = webkit_dom_node_clone_node_with_error (block, TRUE, NULL); + } + + if (citation_level == 1) { + gchar *inner_html; + WebKitDOMElement *paragraph, *element; + + inner_html = webkit_dom_element_get_inner_html (WEBKIT_DOM_ELEMENT (block)); + webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (block), "-x-evo-to-remove"); + + paragraph = e_editor_dom_insert_new_line_into_citation (editor_page, inner_html); + g_free (inner_html); + + if (paragraph) { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (paragraph), + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (paragraph)), + NULL); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (paragraph), + WEBKIT_DOM_NODE (selection_end_marker), + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (paragraph)), + NULL); + + e_editor_dom_remove_quoting_from_element (paragraph); + e_editor_dom_remove_wrapping_from_element (paragraph); + + /* Moving PRE block from citation to body */ + if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block)) { + WebKitDOMElement *pre; + WebKitDOMNode *child; + + pre = webkit_dom_document_create_element (document, "pre", NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (paragraph)), + WEBKIT_DOM_NODE (pre), + WEBKIT_DOM_NODE (paragraph), + NULL); + + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph)))) + webkit_dom_node_append_child (WEBKIT_DOM_NODE (pre), child, NULL); + + remove_node (WEBKIT_DOM_NODE (paragraph)); + paragraph = pre; + } + } + + if (block) + remove_node (block); + + while ((element = webkit_dom_document_get_element_by_id (document, "-x-evo-to-remove"))) + remove_node (WEBKIT_DOM_NODE (element)); + + if (paragraph) + remove_node_if_empty ( + webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (paragraph))); + } + + if (citation_level > 1) { + WebKitDOMNode *parent; + + if (html_mode) { + webkit_dom_node_insert_before ( + block, + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_first_child (block), + NULL); + webkit_dom_node_insert_before ( + block, + WEBKIT_DOM_NODE (selection_end_marker), + webkit_dom_node_get_first_child (block), + NULL); + + } + + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block)); + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block)); + + parent = webkit_dom_node_get_parent_node (block); + + if (!webkit_dom_node_get_previous_sibling (block)) { + /* Currect block is in the beginning of citation, just move it + * before the citation where already is */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + block, + parent, + NULL); + } else if (!webkit_dom_node_get_next_sibling (block)) { + /* Currect block is at the end of the citation, just move it + * after the citation where already is */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + block, + webkit_dom_node_get_next_sibling (parent), + NULL); + } else { + /* Current block is somewhere in the middle of the citation + * so we need to split the citation and insert the block into + * the citation that is one level lower */ + WebKitDOMNode *clone, *child; + + clone = webkit_dom_node_clone_node_with_error (parent, FALSE, NULL); + + /* Move nodes that are after the currect block into the + * new blockquote */ + child = webkit_dom_node_get_next_sibling (block); + while (child) { + WebKitDOMNode *next = webkit_dom_node_get_next_sibling (child); + webkit_dom_node_append_child (clone, child, NULL); + child = next; + } + + clone = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + clone, + webkit_dom_node_get_next_sibling (parent), + NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + block, + clone, + NULL); + } + + e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (block)); + } + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + return success; +} + +static gboolean +prevent_from_deleting_last_element_in_body (WebKitDOMDocument *document) +{ + gboolean ret_val = FALSE; + WebKitDOMHTMLElement *body; + WebKitDOMNode *node; + + body = webkit_dom_document_get_body (document); + + node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + if (!node || !webkit_dom_node_get_next_sibling (node)) { + gchar *content; + + content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (body)); + + if (!content || !*content) + ret_val = TRUE; + + g_free (content); + + if (webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (body), "img", NULL)) + ret_val = FALSE; + } + + return ret_val; +} + +static void +insert_quote_symbols (WebKitDOMDocument *document, + WebKitDOMHTMLElement *element, + gint quote_level) +{ + gchar *quotation; + WebKitDOMElement *quote_element; + + if (!WEBKIT_DOM_IS_ELEMENT (element)) + return; + + quotation = get_quotation_for_level (quote_level); + + quote_element = webkit_dom_document_create_element (document, "span", NULL); + element_add_class (quote_element, "-x-evo-quoted"); + + webkit_dom_element_set_inner_html (quote_element, quotation, NULL); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (quote_element), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)), + NULL); + + g_free (quotation); +} + +static void +quote_node (WebKitDOMDocument *document, + WebKitDOMNode *node, + gint quote_level) +{ + WebKitDOMNode *parent, *next_sibling; + + /* Don't quote when we are not in citation */ + if (quote_level == 0) + return; + + if (WEBKIT_DOM_IS_COMMENT (node)) + return; + + if (WEBKIT_DOM_IS_ELEMENT (node)) { + insert_quote_symbols (document, WEBKIT_DOM_HTML_ELEMENT (node), quote_level); + return; + } + + next_sibling = webkit_dom_node_get_next_sibling (node); + + /* Skip the BR between first blockquote and pre */ + if (quote_level == 1 && next_sibling && WEBKIT_DOM_IS_HTML_PRE_ELEMENT (next_sibling)) + return; + + parent = webkit_dom_node_get_parent_node (node); + + insert_quote_symbols ( + document, WEBKIT_DOM_HTML_ELEMENT (parent), quote_level); +} + +static void +insert_quote_symbols_before_node (WebKitDOMDocument *document, + WebKitDOMNode *node, + gint quote_level, + gboolean is_html_node) +{ + gboolean skip, wrap_br; + gchar *quotation; + WebKitDOMElement *element; + + quotation = get_quotation_for_level (quote_level); + element = webkit_dom_document_create_element (document, "SPAN", NULL); + element_add_class (element, "-x-evo-quoted"); + webkit_dom_element_set_inner_html (element, quotation, NULL); + + /* Don't insert temporary BR before BR that is used for wrapping */ + skip = WEBKIT_DOM_IS_HTML_BR_ELEMENT (node); + wrap_br = element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br"); + skip = skip && wrap_br; + + if (is_html_node && !skip) { + WebKitDOMElement *new_br; + + new_br = webkit_dom_document_create_element (document, "br", NULL); + element_add_class (new_br, "-x-evo-temp-br"); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (new_br), + node, + NULL); + } + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + node, + NULL); + + if (is_html_node && !wrap_br) + remove_node (node); + + g_free (quotation); +} + +static gboolean +check_if_suppress_next_node (WebKitDOMNode *node) +{ + if (!node) + return FALSE; + + if (node && WEBKIT_DOM_IS_ELEMENT (node)) + if (e_editor_dom_is_selection_position_node (node)) + if (!webkit_dom_node_get_previous_sibling (node)) + return FALSE; + + return TRUE; +} + +static void +quote_br_node (WebKitDOMNode *node, + gint quote_level) +{ + gchar *quotation, *content; + + quotation = get_quotation_for_level (quote_level); + + content = g_strconcat ( + "<span class=\"-x-evo-quoted\">", + quotation, + "</span><br class=\"-x-evo-temp-br\">", + NULL); + + webkit_dom_element_set_outer_html ( + WEBKIT_DOM_ELEMENT (node), + content, + NULL); + + g_free (content); + g_free (quotation); +} + +static void +quote_plain_text_recursive (WebKitDOMDocument *document, + WebKitDOMNode *block, + WebKitDOMNode *start_node, + gint quote_level) +{ + gboolean skip_node = FALSE; + gboolean move_next = FALSE; + gboolean suppress_next = FALSE; + gboolean is_html_node = FALSE; + gboolean next = FALSE; + WebKitDOMNode *node, *next_sibling, *prev_sibling; + + node = webkit_dom_node_get_first_child (block); + + while (node) { + gchar *text_content; + + skip_node = FALSE; + move_next = FALSE; + is_html_node = FALSE; + + if (WEBKIT_DOM_IS_COMMENT (node) || + WEBKIT_DOM_IS_HTML_META_ELEMENT (node) || + WEBKIT_DOM_IS_HTML_STYLE_ELEMENT (node) || + WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node)) { + + move_next = TRUE; + goto next_node; + } + + prev_sibling = webkit_dom_node_get_previous_sibling (node); + next_sibling = webkit_dom_node_get_next_sibling (node); + + if (WEBKIT_DOM_IS_TEXT (node)) { + /* Start quoting after we are in blockquote */ + if (quote_level > 0 && !suppress_next) { + /* When quoting text node, we are wrappering it and + * afterwards replacing it with that wrapper, thus asking + * for next_sibling after quoting will return NULL bacause + * that node don't exist anymore */ + quote_node (document, node, quote_level); + node = next_sibling; + skip_node = TRUE; + } + + goto next_node; + } + + if (!(WEBKIT_DOM_IS_ELEMENT (node) || WEBKIT_DOM_IS_HTML_ELEMENT (node))) + goto next_node; + + if (e_editor_dom_is_selection_position_node (node)) { + /* If there is collapsed selection in the beginning of line + * we cannot suppress first text that is after the end of + * selection */ + suppress_next = check_if_suppress_next_node (prev_sibling); + if (suppress_next) + next = FALSE; + move_next = TRUE; + goto next_node; + } + + if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) && + webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node)) != 0) + goto with_children; + + /* Even in plain text mode we can have some basic html element + * like anchor and others. When Forwaring e-mail as Quoted EMFormat + * generates header that contatains <b> tags (bold font). + * We have to treat these elements separately to avoid + * modifications of theirs inner texts */ + is_html_node = + WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) || + element_has_tag (WEBKIT_DOM_ELEMENT (node), "b") || + element_has_tag (WEBKIT_DOM_ELEMENT (node), "i") || + element_has_tag (WEBKIT_DOM_ELEMENT (node), "u") || + element_has_class (WEBKIT_DOM_ELEMENT (node), "Apple-tab-span"); + + if (is_html_node) { + gboolean wrap_br; + + wrap_br = + prev_sibling && + WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling) && + element_has_class ( + WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-wrap-br"); + + if (!prev_sibling || wrap_br) { + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + if (!prev_sibling && next_sibling && WEBKIT_DOM_IS_TEXT (next_sibling)) + suppress_next = TRUE; + } + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling) && !wrap_br) + insert_quote_symbols_before_node ( + document, prev_sibling, quote_level, TRUE); + + move_next = TRUE; + goto next_node; + } + + /* If element doesn't have children, we can quote it */ + if (e_editor_dom_node_is_citation_node (node)) { + /* Citation with just text inside */ + quote_node (document, node, quote_level + 1); + + move_next = TRUE; + goto next_node; + } + + if (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) { + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) { + move_next = TRUE; + goto next_node; + } + goto not_br; + } else if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-first-br") || + element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-last-br")) { + quote_br_node (node, quote_level); + node = next_sibling; + skip_node = TRUE; + goto next_node; + } + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling)) { + quote_br_node (prev_sibling, quote_level); + node = next_sibling; + skip_node = TRUE; + goto next_node; + } + + if (!prev_sibling && !next_sibling) { + WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); + + if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) || + WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent) || + (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && + !e_editor_dom_node_is_citation_node (parent))) { + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + + goto next_node; + } + } + + if (e_editor_dom_node_is_citation_node (prev_sibling)) { + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + goto next_node; + } + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node) && + !next_sibling && WEBKIT_DOM_IS_ELEMENT (prev_sibling) && + e_editor_dom_is_selection_position_node (prev_sibling)) { + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + goto next_node; + } + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) { + move_next = TRUE; + goto next_node; + } + + not_br: + text_content = webkit_dom_node_get_text_content (node); + if (text_content && !*text_content) { + g_free (text_content); + move_next = TRUE; + goto next_node; + } + g_free (text_content); + + quote_node (document, node, quote_level); + + move_next = TRUE; + goto next_node; + + with_children: + if (e_editor_dom_node_is_citation_node (node)) { + /* Go deeper and increase level */ + quote_plain_text_recursive ( + document, node, start_node, quote_level + 1); + move_next = TRUE; + } else { + quote_plain_text_recursive ( + document, node, start_node, quote_level); + move_next = TRUE; + } + next_node: + if (next) { + suppress_next = FALSE; + next = FALSE; + } + + if (suppress_next) + next = TRUE; + + if (!skip_node) { + /* Move to next node */ + if (!move_next && webkit_dom_node_has_child_nodes (node)) { + node = webkit_dom_node_get_first_child (node); + } else if (webkit_dom_node_get_next_sibling (node)) { + node = webkit_dom_node_get_next_sibling (node); + } else { + return; + } + } + } +} + +WebKitDOMElement * +e_editor_dom_quote_plain_text_element (EEditorPage *editor_page, + WebKitDOMElement *element) +{ + WebKitDOMDocument *document; + WebKitDOMNode *element_clone; + WebKitDOMNodeList *list = NULL; + gint ii, length, level; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + element_clone = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element), TRUE, NULL); + level = e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element), TRUE); + + /* Remove old quote characters if the exists */ + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (element_clone), "span.-x-evo-quoted", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); + + webkit_dom_node_normalize (element_clone); + quote_plain_text_recursive ( + document, element_clone, element_clone, level); + + /* Replace old element with one, that is quoted */ + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + element_clone, + WEBKIT_DOM_NODE (element), + NULL); + + return WEBKIT_DOM_ELEMENT (element_clone); +} + +/* + * dom_quote_plain_text: + * + * Quote text inside citation blockquotes in plain text mode. + * + * As this function is cloning and replacing all citation blockquotes keep on + * mind that any pointers to nodes inside these blockquotes will be invalidated. + */ +static WebKitDOMElement * +dom_quote_plain_text (WebKitDOMDocument *document) +{ + WebKitDOMHTMLElement *body; + WebKitDOMNode *body_clone; + WebKitDOMNamedNodeMap *attributes = NULL; + WebKitDOMNodeList *list = NULL; + WebKitDOMElement *element; + gint ii, length; + gulong attributes_length; + + /* Check if the document is already quoted */ + element = webkit_dom_document_query_selector ( + document, ".-x-evo-quoted", NULL); + if (element) + return NULL; + + body = webkit_dom_document_get_body (document); + body_clone = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), TRUE, NULL); + + /* Clean unwanted spaces before and after blockquotes */ + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (body_clone), "blockquote[type|=cite]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *blockquote = webkit_dom_node_list_item (list, ii); + WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (blockquote); + WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (blockquote); + + if (prev_sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling)) + remove_node (prev_sibling); + + if (next_sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling)) + remove_node (next_sibling); + + if (webkit_dom_node_has_child_nodes (blockquote)) { + WebKitDOMNode *child = webkit_dom_node_get_first_child (blockquote); + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)) + remove_node (child); + } + g_object_unref (blockquote); + } + g_clear_object (&list); + + webkit_dom_node_normalize (body_clone); + quote_plain_text_recursive (document, body_clone, body_clone, 0); + + /* Copy attributes */ + attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); + attributes_length = webkit_dom_named_node_map_get_length (attributes); + for (ii = 0; ii < attributes_length; ii++) { + gchar *name, *value; + WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); + + name = webkit_dom_node_get_local_name (node); + value = webkit_dom_node_get_node_value (node); + + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body_clone), name, value, NULL); + + g_object_unref (node); + g_free (name); + g_free (value); + } + g_clear_object (&attributes); + + /* Replace old BODY with one, that is quoted */ + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)), + body_clone, + WEBKIT_DOM_NODE (body), + NULL); + + return WEBKIT_DOM_ELEMENT (body_clone); +} + +/* + * dom_dequote_plain_text: + * + * Dequote already quoted plain text in editor. + * Editor have to be quoted with e_html_editor_view_quote_plain_text otherwise + * it's not working. + */ +static void +dom_dequote_plain_text (WebKitDOMDocument *document) +{ + WebKitDOMNodeList *paragraphs = NULL; + gint length, ii; + + paragraphs = webkit_dom_document_query_selector_all ( + document, "blockquote[type=cite]", NULL); + length = webkit_dom_node_list_get_length (paragraphs); + for (ii = 0; ii < length; ii++) { + WebKitDOMElement *element; + + element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs, ii)); + + if (e_editor_dom_node_is_citation_node (WEBKIT_DOM_NODE (element))) + e_editor_dom_remove_quoting_from_element (element); + + g_object_unref (element); + } + g_clear_object (¶graphs); +} + +static gboolean +create_anchor_for_link (const GMatchInfo *info, + GString *res, + gpointer data) +{ + gboolean link_surrounded, with_nbsp = FALSE; + gint offset = 0, truncate_from_end = 0; + gint match_start, match_end; + gchar *match_with_nbsp, *match_without_nbsp; + const gchar *end_of_match = NULL; + const gchar *match, *match_extra_characters; + + match_with_nbsp = g_match_info_fetch (info, 1); + /* E-mail addresses will be here. */ + match_without_nbsp = g_match_info_fetch (info, 0); + + if (!match_with_nbsp || (strstr (match_with_nbsp, " ") && !g_str_has_prefix (match_with_nbsp, " "))) { + match = match_without_nbsp; + match_extra_characters = match_with_nbsp; + g_match_info_fetch_pos (info, 0, &match_start, &match_end); + with_nbsp = TRUE; + } else { + match = match_with_nbsp; + match_extra_characters = match_without_nbsp; + g_match_info_fetch_pos (info, 1, &match_start, &match_end); + } + + if (g_str_has_prefix (match, " ")) + offset += 6; + + end_of_match = match + match_end - match_start - 1; + /* Taken from camel-url-scanner.c */ + /* URLs are extremely unlikely to end with any punctuation, so + * strip any trailing punctuation off from link and put it after + * the link. Do the same for any closing double-quotes as well. */ + while (end_of_match && end_of_match != match && strchr (URL_INVALID_TRAILING_CHARS, *end_of_match)) { + truncate_from_end++; + end_of_match--; + } + end_of_match++; + + link_surrounded = + g_str_has_suffix (res->str, "<"); + + if (link_surrounded) { + if (end_of_match && *end_of_match && strlen (match) > strlen (end_of_match) + 3) + link_surrounded = link_surrounded && g_str_has_prefix (end_of_match - 3, ">"); + else + link_surrounded = link_surrounded && g_str_has_suffix (match, ">"); + + if (link_surrounded) { + /* ";" is already counted by code above */ + truncate_from_end += 3; + end_of_match -= 3; + } + } + + g_string_append (res, "<a href=\""); + if (strstr (match, "@") && !strstr (match, "://")) + g_string_append (res, "mailto:"); + g_string_append (res, match + offset); + if (truncate_from_end > 0) + g_string_truncate (res, res->len - truncate_from_end); + + g_string_append (res, "\">"); + g_string_append (res, match + offset); + if (truncate_from_end > 0) + g_string_truncate (res, res->len - truncate_from_end); + + g_string_append (res, "</a>"); + + if (truncate_from_end > 0) + g_string_append (res, end_of_match); + + if (!with_nbsp && match_extra_characters) + g_string_append (res, match_extra_characters + (match_end - match_start)); + + g_free (match_with_nbsp); + g_free (match_without_nbsp); + + return FALSE; +} + +static gboolean +replace_to_nbsp (const GMatchInfo *info, + GString *res) +{ + gchar *match; + gint ii = 0; + + match = g_match_info_fetch (info, 0); + + while (match[ii] != '\0') { + if (match[ii] == ' ') { + /* Alone spaces or spaces before/after tabulator. */ + g_string_append (res, " "); + } else if (match[ii] == '\t') { + /* Replace tabs with their WebKit HTML representation. */ + g_string_append (res, "<span class=\"Apple-tab-span\" style=\"white-space:pre\">\t</span>"); + } + + ii++; + } + + g_free (match); + + return FALSE; +} + +static gboolean +surround_links_with_anchor (const gchar *text) +{ + return (strstr (text, "http") || strstr (text, "ftp") || + strstr (text, "www") || strstr (text, "@")); +} + +static void +append_new_block (WebKitDOMElement *parent, + WebKitDOMElement **block) +{ + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (parent), + WEBKIT_DOM_NODE (*block), + NULL); + + *block = NULL; +} + +static WebKitDOMElement * +create_and_append_new_block (EEditorPage *editor_page, + WebKitDOMElement *parent, + WebKitDOMElement *block_template, + const gchar *content) +{ + WebKitDOMElement *block; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + block = WEBKIT_DOM_ELEMENT (webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (block_template), FALSE, NULL)); + + webkit_dom_element_set_inner_html (block, content, NULL); + + append_new_block (parent, &block); + + return block; +} + +static void +append_citation_mark (WebKitDOMDocument *document, + WebKitDOMElement *parent, + const gchar *citation_mark_text) +{ + WebKitDOMText *text; + + text = webkit_dom_document_create_text_node (document, citation_mark_text); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (parent), + WEBKIT_DOM_NODE (text), + NULL); +} + +static void +replace_selection_markers (gchar **text) +{ + if (!text) + return; + + if (strstr (*text, "##SELECTION_START##")) { + GString *tmp; + + tmp = e_str_replace_string ( + *text, + "##SELECTION_START##", + "<span id=\"-x-evo-selection-start-marker\"></span>"); + + g_free (*text); + *text = g_string_free (tmp, FALSE); + } + + if (strstr (*text, "##SELECTION_END##")) { + GString *tmp; + + tmp = e_str_replace_string ( + *text, + "##SELECTION_END##", + "<span id=\"-x-evo-selection-end-marker\"></span>"); + + g_free (*text); + *text = g_string_free (tmp, FALSE); + } +} + +static GString * +remove_new_lines_around_citations (const gchar *input) +{ + GString *str = NULL; + const gchar *p, *next; + + str = g_string_new (""); + + /* Remove the new lines around citations: + * Replace <br><br>##CITATION_START## with <br>##CITATION_START## + * Replace ##CITATION_START##<br><br> with ##CITATION_START##<br> + * Replace <br>##CITATION_END## with ##CITATION_END## */ + p = input; + while (next = strstr (p, "##CITATION_"), next) { + gchar citation_type = 0; + + if (p < next) + g_string_append_len (str, p, next - p); + + if (next + 11) + citation_type = next[11]; + /* ##CITATION_START## */ + if (citation_type == 'S') { + if (g_str_has_suffix (str->str, "<br><br>") || + g_str_has_suffix (str->str, "<br><br>")) + g_string_truncate (str, str->len - 4); + + if (g_str_has_prefix (next + 11, "START##<br><br>")) { + g_string_append (str, "##CITATION_START##<br>"); + p = next + 26; + continue; + } + } else if (citation_type == 'E') { + if (g_str_has_suffix (str->str, "<br>")) + g_string_truncate (str, str->len - 4); + } + + g_string_append (str, "##CITATION_"); + + p = next + 11; + } + + g_string_append (str, p); + + return str; +} + +static GString * +replace_citation_marks_to_citations (const gchar *input) +{ + GString *str = NULL; + const gchar *p, *next; + + str = g_string_new (""); + + /* Replaces text markers with actual HTML blockquotes */ + p = input; + while (next = strstr (p, "##CITATION_"), next) { + gchar citation_type = 0; + + if (p < next) + g_string_append_len (str, p, next - p); + + if (next + 11) + citation_type = next[11]; + /* ##CITATION_START## */ + if (citation_type == 'S') { + g_string_append (str, "<blockquote type=\"cite\">"); + p = next + 18; + } else if (citation_type == 'E') { + g_string_append (str, "</blockquote>"); + p = next + 16; + } else + p = next + 11; + } + + g_string_append (str, p); + + return str; +} + +/* This parses the HTML code (that contains just text, and BR elements) + * into blocks. + * HTML code in that format we can get by taking innerText from some element, + * setting it to another one and finally getting innerHTML from it */ +static void +parse_html_into_blocks (EEditorPage *editor_page, + WebKitDOMElement *parent, + WebKitDOMElement *passed_block_template, + const gchar *input) +{ + gboolean has_citation = FALSE, processing_last = FALSE; + const gchar *prev_br, *next_br; + GString *html = NULL; + GRegex *regex_nbsp = NULL, *regex_link = NULL, *regex_email = NULL; + WebKitDOMDocument *document; + WebKitDOMElement *block_template = passed_block_template; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + webkit_dom_element_set_inner_html (parent, "", NULL); + + if (!block_template) { + gboolean use_paragraphs; + GSettings *settings; + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + + use_paragraphs = g_settings_get_boolean ( + settings, "composer-wrap-quoted-text-in-replies"); + + if (use_paragraphs) + block_template = e_editor_dom_get_paragraph_element (editor_page, -1, 0); + else + block_template = webkit_dom_document_create_element (document, "pre", NULL); + + g_object_unref (settings); + } + + /* Replace the tabulators with SPAN elements that corresponds to them. + * If not inserting the content into the PRE element also replace single + * spaces on the beginning of line, 2+ spaces and with non breaking + * spaces. */ + if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block_template)) + regex_nbsp = g_regex_new ("\x9", 0, 0, NULL); + else + regex_nbsp = g_regex_new ("^\\s{1}|\\s{2,}|\x9|\\s$", 0, 0, NULL); + + + html = remove_new_lines_around_citations (input); + + prev_br = html->str; + next_br = strstr (prev_br, "<br>"); + processing_last = !next_br; + + while (next_br || processing_last) { + const gchar *citation_start = NULL, *citation_end = NULL; + const gchar *rest = NULL, *with_br = NULL; + gchar *to_process = NULL, *to_insert = NULL; + guint to_insert_start = 0, to_insert_end = 0; + + if (!next_br) { + to_process = g_strdup (prev_br); + processing_last = TRUE; + } else if ((to_process = g_utf8_substring (prev_br, 0, g_utf8_pointer_to_offset (prev_br, next_br))) && !*to_process && !processing_last) { + g_free (to_process); + to_process = g_strdup (next_br); + processing_last = TRUE; + } + to_insert_end = g_utf8_strlen (to_process, -1); + + if ((with_br = strstr (to_process, "<br>"))) { + if (with_br == to_process) + to_insert_start += 4; + } + if ((citation_start = strstr (to_process, "##CITATION_START"))) { + if (with_br && citation_start == with_br + 4) + to_insert_start += 18; /* + ## */ + else + to_insert_end -= 18; /* + ## */ + has_citation = TRUE; + } + if ((citation_end = strstr (to_process, "##CITATION_END"))) + to_insert_end -= 16; /* + ## */ + + /* First BR */ + if (with_br && prev_br == html->str) + create_and_append_new_block ( + editor_page, parent, block_template, "<br id=\"-x-evo-first-br\">"); + + if (with_br && citation_start && citation_start == with_br + 4) { + create_and_append_new_block ( + editor_page, parent, block_template, "<br>"); + + append_citation_mark (document, parent, "##CITATION_START##"); + } + + if ((to_insert = g_utf8_substring (to_process, to_insert_start, to_insert_end)) && *to_insert) { + gboolean empty = FALSE; + gchar *truncated = g_strdup (to_insert); + gchar *rest_to_insert; + + empty = !*truncated && strlen (to_insert) > 0; + + rest_to_insert = g_regex_replace_eval ( + regex_nbsp, + empty ? rest : truncated, + -1, + 0, + 0, + (GRegexEvalCallback) replace_to_nbsp, + NULL, + NULL); + g_free (truncated); + + replace_selection_markers (&rest_to_insert); + + if (surround_links_with_anchor (rest_to_insert)) { + gboolean is_email_address = + strstr (rest_to_insert, "@") && + !strstr (rest_to_insert, "://"); + + if (is_email_address && !regex_email) + regex_email = g_regex_new (E_MAIL_PATTERN, 0, 0, NULL); + if (!is_email_address && !regex_link) + regex_link = g_regex_new (URL_PATTERN, 0, 0, NULL); + + truncated = g_regex_replace_eval ( + is_email_address ? regex_email : regex_link, + rest_to_insert, + -1, + 0, + G_REGEX_MATCH_NOTEMPTY, + create_anchor_for_link, + NULL, + NULL); + + g_free (rest_to_insert); + rest_to_insert = truncated; + } + + create_and_append_new_block ( + editor_page, parent, block_template, rest_to_insert); + + g_free (rest_to_insert); + } else if (to_insert && !citation_start) + create_and_append_new_block ( + editor_page, parent, block_template, "<br>"); + + g_free (to_insert); + + if (with_br && citation_start && citation_start != with_br + 4) + append_citation_mark (document, parent, "##CITATION_START##"); + + if (citation_end) + append_citation_mark (document, parent, "##CITATION_END##"); + + g_free (to_process); + + prev_br = next_br; + next_br = (prev_br && *prev_br) ? strstr (prev_br + 1, "<br>") : NULL; + if (!next_br && !processing_last) { + if (!prev_br) + break; + + if (g_utf8_strlen (prev_br, -1) > 4) { + next_br = prev_br; + } else { + WebKitDOMNode *child; + + child = webkit_dom_node_get_last_child ( + WEBKIT_DOM_NODE (parent)); + if (child) { + child = webkit_dom_node_get_first_child (child); + if (child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)) { + /* If the processed HTML contained just + * the BR don't overwrite its id. */ + if (!element_has_id (WEBKIT_DOM_ELEMENT (child), "-x-evo-first-br")) + webkit_dom_element_set_id ( + WEBKIT_DOM_ELEMENT (child), + "-x-evo-last-br"); + } else if (!webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL)) { + /* FIXME WK2 - the signature could not be inserted at this point + * this is the reason why there is an extra NL on the very end of + * quoted text in reply. */ + create_and_append_new_block ( + editor_page, parent, block_template, "<br>"); + } + } else { + create_and_append_new_block ( + editor_page, parent, block_template, "<br>"); + } + break; + } + processing_last = TRUE; + } else if (processing_last && !prev_br && !next_br) { + break; + } + } + + if (has_citation) { + gchar *inner_html; + GString *parsed; + + /* Replace text markers with actual HTML blockquotes */ + inner_html = webkit_dom_element_get_inner_html (parent); + parsed = replace_citation_marks_to_citations (inner_html); + webkit_dom_element_set_inner_html (parent, parsed->str, NULL); + + g_free (inner_html); + g_string_free (parsed, TRUE); + } + + g_string_free (html, TRUE); + + if (regex_email != NULL) + g_regex_unref (regex_email); + if (regex_link != NULL) + g_regex_unref (regex_link); + g_regex_unref (regex_nbsp); +} + +void +e_editor_dom_quote_and_insert_text_into_selection (EEditorPage *editor_page, + const gchar *text, + gboolean is_html) +{ + WebKitDOMDocument *document; + WebKitDOMElement *blockquote, *element, *selection_start; + WebKitDOMNode *node; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + gchar *inner_html; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!text || !*text) + return; + + document = e_editor_page_get_document (editor_page); + + if (is_html) { + element = webkit_dom_document_create_element (document, "div", NULL); + + if (strstr (text, "\n")) { + GRegex *regex; + gchar *tmp; + + /* Strip new lines between tags to avoid unwanted line breaks. */ + regex = g_regex_new ("\\>[\\s]+\\<", 0, 0, NULL); + tmp = g_regex_replace (regex, text, -1, 0, "> <", 0, NULL); + webkit_dom_element_set_inner_html (element, tmp, NULL); + g_free (tmp); + g_regex_unref (regex); + } else { + webkit_dom_element_set_inner_html (element, text, NULL); + } + } else { + /* This is a trick to escape any HTML characters (like <, > or &). + * <textarea> automatically replaces all these unsafe characters + * by <, > etc. */ + element = webkit_dom_document_create_element (document, "textarea", NULL); + webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element), text, NULL); + } + + inner_html = webkit_dom_element_get_inner_html (element); + + e_editor_dom_selection_save (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_PASTE_QUOTED; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->data.string.from = NULL; + ev->data.string.to = g_strdup (text); + } + + blockquote = webkit_dom_document_create_element (document, "blockquote", NULL); + webkit_dom_element_set_attribute (blockquote, "type", "cite", NULL); + + selection_start = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start)); + /* Check if block is empty. If so, replace it otherwise insert the quoted + * content after current block. */ + if (!node || WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) { + node = webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (selection_start)); + node = webkit_dom_node_get_next_sibling (node); + if (!node || WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) { + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start))), + WEBKIT_DOM_NODE (blockquote), + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start)), + NULL); + } + } else { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)), + WEBKIT_DOM_NODE (blockquote), + webkit_dom_node_get_next_sibling ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start))), + NULL); + } + + parse_html_into_blocks (editor_page, blockquote, NULL, inner_html); + + if (e_editor_page_get_html_mode (editor_page)) { + node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (blockquote)); + } else { + gint word_wrap_length; + + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (blockquote)); + while (node) { + WebKitDOMNode *next_sibling; + + node = WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length (editor_page, WEBKIT_DOM_ELEMENT (node), word_wrap_length - 2)); + + webkit_dom_node_normalize (node); + e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT (node), 1); + + next_sibling = webkit_dom_node_get_next_sibling (node); + if (!next_sibling) + break; + + node = next_sibling; + } + } + + dom_add_selection_markers_into_element_end ( + document, WEBKIT_DOM_ELEMENT (node), NULL, NULL); + + e_editor_dom_selection_restore (editor_page); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_force_spell_check_in_viewport (editor_page); + e_editor_page_emit_content_changed (editor_page); + + g_free (inner_html); +} + +static void +mark_citation (WebKitDOMElement *citation) +{ + webkit_dom_html_element_insert_adjacent_text ( + WEBKIT_DOM_HTML_ELEMENT (citation), + "beforebegin", + "##CITATION_START##", + NULL); + + webkit_dom_html_element_insert_adjacent_text ( + WEBKIT_DOM_HTML_ELEMENT (citation), + "afterend", + "##CITATION_END##", + NULL); + + element_add_class (citation, "marked"); +} + +static gint +create_text_markers_for_citations_in_element (WebKitDOMElement *element) +{ + gint count = 0; + WebKitDOMElement *citation; + + citation = webkit_dom_element_query_selector ( + element, "blockquote[type=cite]:not(.marked)", NULL); + + while (citation) { + mark_citation (citation); + count ++; + + citation = webkit_dom_element_query_selector ( + element, "blockquote[type=cite]:not(.marked)", NULL); + } + + return count; +} + +static void +create_text_markers_for_selection_in_element (WebKitDOMElement *element) +{ + WebKitDOMElement *selection_marker; + + selection_marker = webkit_dom_element_query_selector ( + element, "#-x-evo-selection-start-marker", NULL); + if (selection_marker) + webkit_dom_html_element_insert_adjacent_text ( + WEBKIT_DOM_HTML_ELEMENT (selection_marker), + "afterend", + "##SELECTION_START##", + NULL); + + selection_marker = webkit_dom_element_query_selector ( + element, "#-x-evo-selection-end-marker", NULL); + if (selection_marker) + webkit_dom_html_element_insert_adjacent_text ( + WEBKIT_DOM_HTML_ELEMENT (selection_marker), + "afterend", + "##SELECTION_END##", + NULL); +} + +static void +quote_plain_text_elements_after_wrapping_in_document (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *list = NULL; + gint length, ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + /* Also quote the PRE elements as well. */ + list = webkit_dom_document_query_selector_all ( + document, "blockquote[type=cite] > p[data-evo-paragraph], blockquote[type=cite] > pre", NULL); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + gint citation_level; + WebKitDOMNode *child; + + child = webkit_dom_node_list_item (list, ii); + citation_level = e_editor_dom_get_citation_level (child, TRUE); + e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT (child), citation_level); + g_object_unref (child); + } + g_clear_object (&list); +} + +static void +clear_attributes (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNamedNodeMap *attributes = NULL; + WebKitDOMHTMLElement *body; + WebKitDOMHTMLHeadElement *head; + WebKitDOMElement *document_element; + gint length, ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + body = webkit_dom_document_get_body (document); + head = webkit_dom_document_get_head (document); + document_element = webkit_dom_document_get_document_element (document); + + /* Remove all attributes from HTML element */ + attributes = webkit_dom_element_get_attributes (document_element); + length = webkit_dom_named_node_map_get_length (attributes); + for (ii = length - 1; ii >= 0; ii--) { + WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); + + webkit_dom_element_remove_attribute_node ( + document_element, WEBKIT_DOM_ATTR (node), NULL); + g_object_unref (node); + } + g_clear_object (&attributes); + + /* Remove everything from HEAD element */ + while (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head))) + remove_node (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head))); + + /* Make the quote marks non-selectable. */ + e_editor_dom_disable_quote_marks_select (editor_page); + + /* Remove non Evolution attributes from BODY element */ + attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); + length = webkit_dom_named_node_map_get_length (attributes); + for (ii = length - 1; ii >= 0; ii--) { + gchar *name; + WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); + + name = webkit_dom_node_get_local_name (node); + + if (!g_str_has_prefix (name, "data-") && (g_strcmp0 (name, "spellcheck") != 0)) + webkit_dom_element_remove_attribute_node ( + WEBKIT_DOM_ELEMENT (body), + WEBKIT_DOM_ATTR (node), + NULL); + + g_object_unref (node); + g_free (name); + } + g_clear_object (&attributes); +} + +static void +body_compositionstart_event_cb (WebKitDOMElement *element, + WebKitDOMUIEvent *event, + EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + e_editor_page_set_composition_in_progress (editor_page, TRUE); + e_editor_dom_remove_input_event_listener_from_body (editor_page); +} + +static void +body_compositionend_event_cb (WebKitDOMElement *element, + WebKitDOMUIEvent *event, + EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + e_editor_page_set_composition_in_progress (editor_page, FALSE); + e_editor_dom_remove_input_event_listener_from_body (editor_page); +} + +static void +register_html_events_handlers (EEditorPage *editor_page, + WebKitDOMHTMLElement *body) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (body), + "keydown", + G_CALLBACK (body_keydown_event_cb), + FALSE, + editor_page); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (body), + "keypress", + G_CALLBACK (body_keypress_event_cb), + FALSE, + editor_page); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (body), + "keyup", + G_CALLBACK (body_keyup_event_cb), + FALSE, + editor_page); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (body), + "compositionstart", + G_CALLBACK (body_compositionstart_event_cb), + FALSE, + editor_page); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (body), + "compositionend", + G_CALLBACK (body_compositionend_event_cb), + FALSE, + editor_page); +} + +void +e_editor_dom_convert_content (EEditorPage *editor_page, + const gchar *preferred_text) +{ + WebKitDOMDocument *document; + WebKitDOMElement *paragraph, *content_wrapper, *top_signature; + WebKitDOMElement *cite_body, *signature, *wrapper; + WebKitDOMHTMLElement *body; + WebKitDOMNodeList *list = NULL; + WebKitDOMNode *node; + WebKitDOMDOMWindow *dom_window = NULL; + gboolean start_bottom, empty = FALSE; + gchar *inner_html; + gint ii, length; + GSettings *settings; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom"); + g_object_unref (settings); + + dom_window = webkit_dom_document_get_default_view (document); + body = webkit_dom_document_get_body (document); + /* Wrapper that will represent the new body. */ + wrapper = webkit_dom_document_create_element (document, "div", NULL); + + cite_body = webkit_dom_document_query_selector ( + document, "span.-x-evo-cite-body", NULL); + + /* content_wrapper when the processed text will be placed. */ + content_wrapper = webkit_dom_document_create_element ( + document, cite_body ? "blockquote" : "div", NULL); + if (cite_body) { + webkit_dom_element_set_attribute (content_wrapper, "type", "cite", NULL); + webkit_dom_element_set_attribute (content_wrapper, "id", "-x-evo-main-cite", NULL); + } + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (content_wrapper), NULL); + + /* Remove all previously inserted paragraphs. */ + list = webkit_dom_document_query_selector_all ( + document, "p[data-evo-paragraph]:not([data-headers])", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); + + /* Insert the paragraph where the caret will be. */ + paragraph = e_editor_dom_prepare_paragraph (editor_page, TRUE); + webkit_dom_element_set_id (paragraph, "-x-evo-input-start"); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (wrapper), + WEBKIT_DOM_NODE (paragraph), + start_bottom ? + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (content_wrapper)) : + WEBKIT_DOM_NODE (content_wrapper), + NULL); + + /* Insert signature (if presented) to the right position. */ + top_signature = webkit_dom_document_query_selector ( + document, ".-x-evo-top-signature", NULL); + signature = webkit_dom_document_query_selector ( + document, ".-x-evo-signature-wrapper", NULL); + if (signature) { + if (top_signature) { + WebKitDOMElement *spacer; + + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (wrapper), + WEBKIT_DOM_NODE (signature), + start_bottom ? + WEBKIT_DOM_NODE (content_wrapper) : + webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (paragraph)), + NULL); + /* Insert NL after the signature */ + spacer = e_editor_dom_prepare_paragraph (editor_page, FALSE); + element_add_class (spacer, "-x-evo-top-signature-spacer"); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (wrapper), + WEBKIT_DOM_NODE (spacer), + webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (signature)), + NULL); + } else { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (wrapper), + WEBKIT_DOM_NODE (signature), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE ( + start_bottom ? paragraph : content_wrapper)), + NULL); + } + } + + /* Move credits to the body */ + list = webkit_dom_document_query_selector_all ( + document, "span.-x-evo-to-body[data-credits]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + char *credits; + WebKitDOMElement *element; + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + element = e_editor_dom_get_paragraph_element (editor_page, -1, 0); + credits = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "data-credits"); + if (credits) + webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element), credits, NULL); + g_free (credits); + + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (wrapper), + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (content_wrapper), + NULL); + + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); + + /* Move headers to body */ + list = webkit_dom_document_query_selector_all ( + document, "div[data-headers]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (list, ii); + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (node), "data-headers"); + e_editor_dom_set_paragraph_style (editor_page, WEBKIT_DOM_ELEMENT (node), -1, 0, NULL); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (wrapper), + node, + WEBKIT_DOM_NODE (content_wrapper), + NULL); + + g_object_unref (node); + } + g_clear_object (&list); + + repair_gmail_blockquotes (document); + remove_thunderbird_signature (document); + create_text_markers_for_citations_in_element (WEBKIT_DOM_ELEMENT (body)); + + if (preferred_text && *preferred_text) + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (content_wrapper), preferred_text, NULL); + else { + gchar *inner_text; + + inner_text = webkit_dom_html_element_get_inner_text (body); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (content_wrapper), inner_text, NULL); + + g_free (inner_text); + } + + inner_html = webkit_dom_element_get_inner_html (content_wrapper); + + /* Replace the old body with the new one. */ + node = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), FALSE, NULL); + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)), + node, + WEBKIT_DOM_NODE (body), + NULL); + body = WEBKIT_DOM_HTML_ELEMENT (node); + + /* Copy all to nodes to the new body. */ + while ((node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (wrapper)))) { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (body), + WEBKIT_DOM_NODE (node), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), + NULL); + } + remove_node (WEBKIT_DOM_NODE (wrapper)); + + if (inner_html && !*inner_html) + empty = TRUE; + + length = webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (body)); + if (length <= 1) { + empty = TRUE; + if (length == 1) { + WebKitDOMNode *child; + + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + empty = child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (child); + } + } + + if (preferred_text && *preferred_text) + empty = FALSE; + + if (!empty) + parse_html_into_blocks (editor_page, content_wrapper, NULL, inner_html); + else + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (content_wrapper), + WEBKIT_DOM_NODE (e_editor_dom_prepare_paragraph (editor_page, FALSE)), + NULL); + + if (!cite_body) { + if (!empty) { + WebKitDOMNode *child; + + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (content_wrapper)))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (content_wrapper)), + child, + WEBKIT_DOM_NODE (content_wrapper), + NULL); + } + } + + remove_node (WEBKIT_DOM_NODE (content_wrapper)); + } + + /* If not editing a message, don't add any new block and just place + * the caret in the beginning of content. We want to have the same + * behaviour when editing message as new or we start replying on top. */ + if (!signature && !start_bottom) { + WebKitDOMNode *child; + + remove_node (WEBKIT_DOM_NODE (paragraph)); + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + if (child) + dom_add_selection_markers_into_element_start ( + document, WEBKIT_DOM_ELEMENT (child), NULL, NULL); + } + + if ((paragraph = webkit_dom_document_get_element_by_id (document, "-x-evo-last-br"))) + webkit_dom_element_remove_attribute (paragraph, "id"); + if ((paragraph = webkit_dom_document_get_element_by_id (document, "-x-evo-first-br"))) + webkit_dom_element_remove_attribute (paragraph, "id"); + + e_editor_dom_merge_siblings_if_necessary (editor_page, NULL); + + if (!e_editor_page_get_html_mode (editor_page)) { + e_editor_dom_wrap_paragraphs_in_document (editor_page); + + quote_plain_text_elements_after_wrapping_in_document (editor_page); + } + + clear_attributes (editor_page); + + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_in_viewport (editor_page); + + /* Register on input event that is called when the content (body) is modified */ + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (body), + "input", + G_CALLBACK (body_input_event_cb), + FALSE, + editor_page); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (dom_window), + "scroll", + G_CALLBACK (body_scroll_event_cb), + FALSE, + editor_page); + + /* Intentionally leak the WebKitDOMDOMWindow object here as otherwise the + * callback won't be set up. */ + + register_html_events_handlers (editor_page, body); + e_editor_dom_set_monospace_font_family_on_body ( + WEBKIT_DOM_ELEMENT (body), e_editor_page_get_html_mode (editor_page)); + + g_free (inner_html); +} + +void +e_editor_dom_convert_and_insert_html_into_selection (EEditorPage *editor_page, + const gchar *html, + gboolean is_html) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker, *element; + WebKitDOMNode *node, *current_block; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + gboolean has_selection; + gchar *inner_html; + gint citation_level; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + e_editor_dom_remove_input_event_listener_from_body (editor_page); + + e_editor_dom_selection_save (editor_page); + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + current_block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block)) + current_block = NULL; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + gboolean collapsed; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_PASTE; +/* FIXME WK2 + ev->type = HISTORY_PASTE_AS_TEXT;*/ + + collapsed = e_editor_dom_selection_is_collapsed (editor_page); + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + if (!collapsed) { + ev->before.end.x = ev->before.start.x; + ev->before.end.y = ev->before.start.y; + } + + ev->data.string.from = NULL; + ev->data.string.to = g_strdup (html); + } + + element = webkit_dom_document_create_element (document, "div", NULL); + if (is_html) { + gchar *inner_text; + + if (strstr (html, "\n")) { + GRegex *regex; + gchar *tmp; + + /* Strip new lines between tags to avoid unwanted line breaks. */ + regex = g_regex_new ("\\>[\\s]+\\<", 0, 0, NULL); + tmp = g_regex_replace ( + regex, html, -1, 0, "> <", 0, NULL); + webkit_dom_element_set_inner_html (element, tmp, NULL); + g_free (tmp); + g_regex_unref (regex); + } else { + webkit_dom_element_set_inner_html (element, html, NULL); + } + + inner_text = webkit_dom_html_element_get_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (element)); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (element), inner_text, NULL); + + g_free (inner_text); + } else + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (element), html, NULL); + + inner_html = webkit_dom_element_get_inner_html (element); + parse_html_into_blocks (editor_page, element, WEBKIT_DOM_ELEMENT (current_block), inner_html); + + g_free (inner_html); + + has_selection = !e_editor_dom_selection_is_collapsed (editor_page); + if (has_selection && !e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + WebKitDOMRange *range = NULL; + + range = e_editor_dom_get_current_range (editor_page); + insert_delete_event (editor_page, range); + g_clear_object (&range); + + /* Remove the text that was meant to be replaced by the pasted text */ + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL); + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + current_block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block)) + current_block = NULL; + } + + citation_level = e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (selection_end_marker), FALSE); + /* Pasting into the citation */ + if (citation_level > 0) { + gint length; + gint word_wrap_length; + WebKitDOMElement *br; + WebKitDOMNode *first_paragraph, *last_paragraph; + WebKitDOMNode *child, *parent, *current_block; + + first_paragraph = webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (element)); + last_paragraph = webkit_dom_node_get_last_child ( + WEBKIT_DOM_NODE (element)); + + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + length = word_wrap_length - 2 * citation_level; + + /* Pasting text that was parsed just into one paragraph */ + if (webkit_dom_node_is_same_node (first_paragraph, last_paragraph)) { + WebKitDOMNode *child, *parent, *parent_block; + + parent_block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent_block)); + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent_block)); + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); + while ((child = webkit_dom_node_get_first_child (first_paragraph))) { + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) && + WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (child)) { + WebKitDOMNode *anchor_child; + + while ((anchor_child = webkit_dom_node_get_first_child (child))) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)), + anchor_child, + WEBKIT_DOM_NODE (selection_start_marker), + NULL); + remove_node (child); + } else + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)), + child, + WEBKIT_DOM_NODE (selection_start_marker), + NULL); + } + + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) { + gchar *text_content; + + text_content = webkit_dom_node_get_text_content (parent); + + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (parent), + "href", + text_content, + NULL); + g_free (text_content); + } + + parent_block = WEBKIT_DOM_NODE ( + e_editor_dom_wrap_paragraph_length (editor_page, WEBKIT_DOM_ELEMENT (parent_block), length)); + webkit_dom_node_normalize (parent_block); + e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT (parent_block), citation_level); + + e_editor_dom_selection_restore (editor_page); + + g_object_unref (element); + goto out; + } + + /* Pasting content parsed into the multiple paragraphs */ + parent = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent)); + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent)); + + /* Move the elements from the first paragraph before the selection start element */ + while ((child = webkit_dom_node_get_first_child (first_paragraph))) + webkit_dom_node_insert_before ( + parent, + child, + WEBKIT_DOM_NODE (selection_start_marker), + NULL); + + remove_node (first_paragraph); + + /* If the BR element is on the last position, remove it as we don't need it */ + child = webkit_dom_node_get_last_child (parent); + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)) + remove_node (child); + + parent = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_end_marker)); + + child = webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (selection_end_marker)); + /* Move the elements that are in the same paragraph as the selection end + * on the end of pasted text, but avoid BR on the end of paragraph */ + while (child) { + WebKitDOMNode *next_child = + webkit_dom_node_get_next_sibling (child); + if (!(!next_child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (child))) + webkit_dom_node_append_child (last_paragraph, child, NULL); + child = next_child; + } + + current_block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + dom_remove_selection_markers (document); + + /* Caret will be restored on the end of pasted text */ + webkit_dom_node_append_child ( + last_paragraph, + WEBKIT_DOM_NODE (dom_create_selection_marker (document, TRUE)), + NULL); + + webkit_dom_node_append_child ( + last_paragraph, + WEBKIT_DOM_NODE (dom_create_selection_marker (document, FALSE)), + NULL); + + /* Insert the paragraph with the end of the pasted text after + * the paragraph that contains the selection end */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + last_paragraph, + webkit_dom_node_get_next_sibling (parent), + NULL); + + /* Wrap, quote and move all paragraphs from pasted text into the body */ + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) { + child = WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length ( + editor_page, WEBKIT_DOM_ELEMENT (child), length)); + e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT (child), citation_level); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (last_paragraph), + child, + last_paragraph, + NULL); + } + + webkit_dom_node_normalize (last_paragraph); + + last_paragraph = WEBKIT_DOM_NODE ( + e_editor_dom_wrap_paragraph_length ( + editor_page, WEBKIT_DOM_ELEMENT (last_paragraph), length)); + e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT (last_paragraph), citation_level); + + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent)); + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent)); + + current_block = WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length ( + editor_page, WEBKIT_DOM_ELEMENT (current_block), length)); + e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT (current_block), citation_level); + + if ((br = webkit_dom_document_get_element_by_id (document, "-x-evo-last-br"))) + webkit_dom_element_remove_attribute (br, "class"); + + if ((br = webkit_dom_document_get_element_by_id (document, "-x-evo-first-br"))) + webkit_dom_element_remove_attribute (br, "class"); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + + g_object_unref (element); + goto out; + } + + remove_node (WEBKIT_DOM_NODE (selection_start_marker)); + remove_node (WEBKIT_DOM_NODE (selection_end_marker)); + + /* If the text to insert was converted just to one block, pass just its + * text to WebKit otherwise WebKit will insert unwanted block with + * extra new line. */ + if (!webkit_dom_node_get_next_sibling (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) + inner_html = webkit_dom_element_get_inner_html ( + WEBKIT_DOM_ELEMENT (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))); + else + inner_html = webkit_dom_element_get_inner_html (WEBKIT_DOM_ELEMENT (element)); + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_HTML, inner_html); + + if (g_str_has_suffix (inner_html, " ")) + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, " "); + + g_free (inner_html); + + g_object_unref (element); + e_editor_dom_selection_save (editor_page); + + element = webkit_dom_document_query_selector ( + document, "* > br#-x-evo-first-br", NULL); + if (element) { + WebKitDOMNode *sibling; + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (element)); + + sibling = webkit_dom_node_get_previous_sibling (parent); + if (sibling) + remove_node (WEBKIT_DOM_NODE (parent)); + else + webkit_dom_element_remove_attribute (element, "class"); + } + + element = webkit_dom_document_query_selector ( + document, "* > br#-x-evo-last-br", NULL); + if (element) { + WebKitDOMNode *parent; + WebKitDOMNode *child; + + parent = webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (element)); + + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)); + if (node) { + node = webkit_dom_node_get_first_child (node); + if (node) { + inner_html = webkit_dom_node_get_text_content (node); + if (g_str_has_prefix (inner_html, UNICODE_NBSP)) + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL); + g_free (inner_html); + } + } + + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + if (has_selection) { + /* Everything after the selection end marker have to be in separate + * paragraph */ + child = webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (selection_end_marker)); + /* Move the elements that are in the same paragraph as the selection end + * on the end of pasted text, but avoid BR on the end of paragraph */ + while (child) { + WebKitDOMNode *next_child = + webkit_dom_node_get_next_sibling (child); + if (!(!next_child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (child))) + webkit_dom_node_append_child (parent, child, NULL); + child = next_child; + } + + remove_node (WEBKIT_DOM_NODE (element)); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_end_marker))), + parent, + webkit_dom_node_get_next_sibling ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_end_marker))), + NULL); + node = parent; + } else { + node = webkit_dom_node_get_next_sibling (parent); + if (!node) { + fix_structure_after_pasting_multiline_content (parent); + if (!webkit_dom_node_get_first_child (parent)) + remove_node (parent); + } + } + + if (node) { + /* Restore caret on the end of pasted text */ + webkit_dom_node_insert_before ( + node, + WEBKIT_DOM_NODE (selection_end_marker), + webkit_dom_node_get_first_child (node), + NULL); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + webkit_dom_node_insert_before ( + node, + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_first_child (node), + NULL); + } + + if (element) + webkit_dom_element_remove_attribute (element, "class"); + + if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)) && !has_selection) + remove_node (parent); + } else { + /* When pasting the content that was copied from the composer, WebKit + * restores the selection wrongly, thus is saved wrongly and we have + * to fix it */ + WebKitDOMNode *block, *parent, *clone1, *clone2; + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + parent = webkit_dom_node_get_parent_node (block); + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (parent), "id"); + + /* Check if WebKit created wrong structure */ + clone1 = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (block), FALSE, NULL); + clone2 = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (parent), FALSE, NULL); + if (webkit_dom_node_is_equal_node (clone1, clone2) || + (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (clone1) && WEBKIT_DOM_IS_HTML_DIV_ELEMENT (clone2) && + !element_has_class (WEBKIT_DOM_ELEMENT (clone2), "-x-evo-indented"))) { + fix_structure_after_pasting_multiline_content (block); + if (g_strcmp0 (html, "\n") == 0) { + WebKitDOMElement *br; + + br = webkit_dom_document_create_element (document, "br", NULL); + webkit_dom_node_append_child ( + parent, WEBKIT_DOM_NODE (br), NULL); + + webkit_dom_node_insert_before ( + parent, + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_last_child (parent), + NULL); + } else if (!webkit_dom_node_get_first_child (parent)) + remove_node (parent); + } + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)), + WEBKIT_DOM_NODE (selection_end_marker), + webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)), + NULL); + } + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + out: + e_editor_dom_check_magic_links (editor_page, FALSE); + e_editor_dom_force_spell_check_in_viewport (editor_page); + e_editor_dom_scroll_to_caret (editor_page); + + e_editor_dom_register_input_event_listener_on_body (editor_page); + + e_editor_page_emit_content_changed (editor_page); +} + +static gint +get_indentation_level (WebKitDOMElement *element) +{ + WebKitDOMElement *parent; + gint level = 0; + + if (!element) + return 0; + + if (element_has_class (element, "-x-evo-indented")) + level++; + + parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element)); + /* Count level of indentation */ + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + if (element_has_class (parent, "-x-evo-indented")) + level++; + + parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent)); + } + + return level; +} + +static void +process_indented_element (WebKitDOMElement *element) +{ + gchar *spaces; + WebKitDOMNode *child; + + if (!element) + return; + + spaces = g_strnfill (4 * get_indentation_level (element), ' '); + + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)); + while (child) { + /* If next sibling is indented blockqoute skip it, + * it will be processed afterwards */ + if (WEBKIT_DOM_IS_ELEMENT (child) && + element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-indented")) + child = webkit_dom_node_get_next_sibling (child); + + if (WEBKIT_DOM_IS_TEXT (child)) { + gchar *text_content; + gchar *indented_text; + + text_content = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (child)); + indented_text = g_strconcat (spaces, text_content, NULL); + + webkit_dom_text_replace_whole_text ( + WEBKIT_DOM_TEXT (child), + indented_text, + NULL); + + g_free (text_content); + g_free (indented_text); + } + + if (!child) + break; + + /* Move to next node */ + if (webkit_dom_node_has_child_nodes (child)) + child = webkit_dom_node_get_first_child (child); + else if (webkit_dom_node_get_next_sibling (child)) + child = webkit_dom_node_get_next_sibling (child); + else { + if (webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (element), child)) + break; + + child = webkit_dom_node_get_parent_node (child); + if (child) + child = webkit_dom_node_get_next_sibling (child); + } + } + g_free (spaces); + + webkit_dom_element_remove_attribute (element, "style"); +} + +static void +process_quote_nodes (WebKitDOMElement *blockquote) +{ + WebKitDOMNodeList *list = NULL; + int jj, length; + + /* Replace quote nodes with symbols */ + list = webkit_dom_element_query_selector_all ( + blockquote, "span.-x-evo-quoted", NULL); + length = webkit_dom_node_list_get_length (list); + for (jj = 0; jj < length; jj++) { + WebKitDOMNode *quoted_node; + gchar *text_content; + + quoted_node = webkit_dom_node_list_item (list, jj); + text_content = webkit_dom_node_get_text_content (quoted_node); + webkit_dom_element_set_outer_html ( + WEBKIT_DOM_ELEMENT (quoted_node), text_content, NULL); + + g_free (text_content); + g_object_unref (quoted_node); + } + g_clear_object (&list); +} + +/* Taken from GtkHTML */ +static gchar * +get_alpha_value (gint value, + gboolean lower) +{ + GString *str; + gchar *rv; + gint add = lower ? 'a' : 'A'; + + str = g_string_new (". "); + + do { + g_string_prepend_c (str, ((value - 1) % 26) + add); + value = (value - 1) / 26; + } while (value); + + rv = str->str; + g_string_free (str, FALSE); + + return rv; +} + +/* Taken from GtkHTML */ +static gchar * +get_roman_value (gint value, + gboolean lower) +{ + GString *str; + const gchar *base = "IVXLCDM"; + gchar *rv; + gint b, r, add = lower ? 'a' - 'A' : 0; + + if (value > 3999) + return g_strdup ("?. "); + + str = g_string_new (". "); + + for (b = 0; value > 0 && b < 7 - 1; b += 2, value /= 10) { + r = value % 10; + if (r != 0) { + if (r < 4) { + for (; r; r--) + g_string_prepend_c (str, base[b] + add); + } else if (r == 4) { + g_string_prepend_c (str, base[b + 1] + add); + g_string_prepend_c (str, base[b] + add); + } else if (r == 5) { + g_string_prepend_c (str, base[b + 1] + add); + } else if (r < 9) { + for (; r > 5; r--) + g_string_prepend_c (str, base[b] + add); + g_string_prepend_c (str, base[b + 1] + add); + } else if (r == 9) { + g_string_prepend_c (str, base[b + 2] + add); + g_string_prepend_c (str, base[b] + add); + } + } + } + + rv = str->str; + g_string_free (str, FALSE); + + return rv; +} + +static void +process_list_to_plain_text (EEditorPage *editor_page, + WebKitDOMElement *element, + gint level, + GString *output) +{ + EContentEditorBlockFormat format; + EContentEditorAlignment alignment; + gint counter = 1; + gboolean empty = TRUE; + gchar *indent_per_level; + WebKitDOMNode *item; + gint word_wrap_length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + indent_per_level = g_strnfill (SPACES_PER_LIST_LEVEL, ' '); + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + format = dom_get_list_format_from_node ( + WEBKIT_DOM_NODE (element)); + + /* Process list items to plain text */ + item = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)); + while (item) { + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (item)) + g_string_append (output, "\n"); + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) { + gchar *space = NULL, *item_str = NULL; + gint ii = 0; + WebKitDOMElement *wrapped; + GString *item_value = g_string_new (""); + + empty = FALSE; + + alignment = e_editor_dom_get_list_alignment_from_node ( + WEBKIT_DOM_NODE (item)); + + wrapped = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (item), ".-x-evo-wrap-br", NULL); + /* Wrapped text */ + if (wrapped) { + WebKitDOMNode *node = webkit_dom_node_get_first_child (item); + GString *line = g_string_new (""); + + while (node) { + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node) && + element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) { + g_string_append (line, "\n"); + /* put spaces before line characters -> wordwraplength - indentation */ + for (ii = 0; ii < level; ii++) + g_string_append (line, indent_per_level); + if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element)) + g_string_append (line, indent_per_level); + g_string_append (item_value, line->str); + g_string_erase (line, 0, -1); + } else { + /* append text from node to line */ + gchar *text_content; + text_content = webkit_dom_node_get_text_content (node); + g_string_append (line, text_content); + g_free (text_content); + } + node = webkit_dom_node_get_next_sibling (node); + } + + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_LEFT) + g_string_append (item_value, line->str); + + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_CENTER) { + gchar *fill = NULL; + gint fill_length; + + fill_length = word_wrap_length - g_utf8_strlen (line->str, -1); + fill_length -= ii * SPACES_PER_LIST_LEVEL; + if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element)) + fill_length += SPACES_PER_LIST_LEVEL; + fill_length /= 2; + + if (fill_length < 0) + fill_length = 0; + + fill = g_strnfill (fill_length, ' '); + + g_string_append (item_value, fill); + g_string_append (item_value, line->str); + g_free (fill); + } + + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_RIGHT) { + gchar *fill = NULL; + gint fill_length; + + fill_length = word_wrap_length - g_utf8_strlen (line->str, -1); + fill_length -= ii * SPACES_PER_LIST_LEVEL; + + if (fill_length < 0) + fill_length = 0; + + fill = g_strnfill (fill_length, ' '); + + g_string_append (item_value, fill); + g_string_append (item_value, line->str); + g_free (fill); + } + g_string_free (line, TRUE); + /* that same here */ + } else { + gchar *text_content = + webkit_dom_node_get_text_content (item); + + g_string_append (item_value, text_content); + g_free (text_content); + } + + if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST) { + space = g_strnfill (SPACES_PER_LIST_LEVEL - 2, ' '); + item_str = g_strdup_printf ( + "%s* %s", space, item_value->str); + g_free (space); + } + + if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST) { + gint length = 1, tmp = counter, spaces_count; + + while ((tmp = tmp / 10) > 1) + length++; + + if (tmp == 1) + length++; + + spaces_count = SPACES_ORDERED_LIST_FIRST_LEVEL - 2 - length; + if (spaces_count > 0) + space = g_strnfill (spaces_count, ' '); + + item_str = g_strdup_printf ( + "%s%d. %s", space && *space ? space : "", counter, item_value->str); + g_free (space); + } + + if (format > E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST) { + gchar *value, spaces_count; + + if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA) + value = get_alpha_value (counter, FALSE); + else + value = get_roman_value (counter, FALSE); + + spaces_count = SPACES_ORDERED_LIST_FIRST_LEVEL - strlen (value); + if (spaces_count > 0) + space = g_strnfill (spaces_count, ' '); + item_str = g_strdup_printf ( + "%s%s%s", space && *space ? space : "" , value, item_value->str); + g_free (space); + g_free (value); + } + + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_LEFT) { + for (ii = 0; ii < level - 1; ii++) { + g_string_append (output, indent_per_level); + } + if (WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (element)) + if (dom_node_find_parent_element (item, "OL")) + g_string_append (output, indent_per_level); + g_string_append (output, item_str); + } + + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_RIGHT) { + if (!wrapped) { + gchar *fill = NULL; + gint fill_length; + + fill_length = word_wrap_length - g_utf8_strlen (item_str, -1); + fill_length -= ii * SPACES_PER_LIST_LEVEL; + + if (fill_length < 0) + fill_length = 0; + + if (g_str_has_suffix (item_str, " ")) + fill_length++; + + fill = g_strnfill (fill_length, ' '); + + g_string_append (output, fill); + g_free (fill); + } + if (g_str_has_suffix (item_str, " ")) + g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 1); + else + g_string_append (output, item_str); + } + + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_CENTER) { + if (!wrapped) { + gchar *fill = NULL; + gint fill_length = 0; + + for (ii = 0; ii < level - 1; ii++) + g_string_append (output, indent_per_level); + + fill_length = word_wrap_length - g_utf8_strlen (item_str, -1); + fill_length -= ii * SPACES_PER_LIST_LEVEL; + if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element)) + fill_length += SPACES_PER_LIST_LEVEL; + fill_length /= 2; + + if (fill_length < 0) + fill_length = 0; + + if (g_str_has_suffix (item_str, " ")) + fill_length++; + + fill = g_strnfill (fill_length, ' '); + + g_string_append (output, fill); + g_free (fill); + } + if (g_str_has_suffix (item_str, " ")) + g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 1); + else + g_string_append (output, item_str); + } + + counter++; + item = webkit_dom_node_get_next_sibling (item); + if (item) + g_string_append (output, "\n"); + + g_free (item_str); + g_string_free (item_value, TRUE); + } else if (node_is_list (item)) { + process_list_to_plain_text ( + editor_page, WEBKIT_DOM_ELEMENT (item), level + 1, output); + item = webkit_dom_node_get_next_sibling (item); + } else { + item = webkit_dom_node_get_next_sibling (item); + } + } + + if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)) && !empty) + g_string_append (output, "\n"); + + g_free (indent_per_level); +} + +static void +remove_base_attributes (WebKitDOMElement *element) +{ + webkit_dom_element_remove_attribute (element, "class"); + webkit_dom_element_remove_attribute (element, "id"); + webkit_dom_element_remove_attribute (element, "name"); +} + +static void +remove_evolution_attributes (WebKitDOMElement *element) +{ + webkit_dom_element_remove_attribute (element, "data-evo-paragraph"); + webkit_dom_element_remove_attribute (element, "data-converted"); + webkit_dom_element_remove_attribute (element, "data-edit-as-new"); + webkit_dom_element_remove_attribute (element, "data-evo-draft"); + webkit_dom_element_remove_attribute (element, "data-inline"); + webkit_dom_element_remove_attribute (element, "data-uri"); + webkit_dom_element_remove_attribute (element, "data-message"); + webkit_dom_element_remove_attribute (element, "data-name"); + webkit_dom_element_remove_attribute (element, "data-new-message"); + webkit_dom_element_remove_attribute (element, "data-user-wrapped"); + webkit_dom_element_remove_attribute (element, "data-evo-plain-text"); + webkit_dom_element_remove_attribute (element, "data-plain-text-style"); + webkit_dom_element_remove_attribute (element, "data-style"); + webkit_dom_element_remove_attribute (element, "spellcheck"); +} + +static void +convert_element_from_html_to_plain_text (EEditorPage *editor_page, + WebKitDOMElement *element, + gboolean *wrap, + gboolean *quote) +{ + WebKitDOMDocument *document; + WebKitDOMElement *top_signature, *signature, *blockquote, *main_blockquote; + WebKitDOMNode *signature_clone, *from; + WebKitDOMNodeList *list = NULL; + gint blockquotes_count, ii, length; + gchar *inner_text, *inner_html; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + top_signature = webkit_dom_element_query_selector ( + element, ".-x-evo-top-signature", NULL); + signature = webkit_dom_element_query_selector ( + element, "span.-x-evo-signature", NULL); + main_blockquote = webkit_dom_element_query_selector ( + element, "#-x-evo-main-cite", NULL); + + blockquote = webkit_dom_document_create_element ( + document, "blockquote", NULL); + + if (main_blockquote) { + webkit_dom_element_set_attribute ( + blockquote, "type", "cite", NULL); + from = WEBKIT_DOM_NODE (main_blockquote); + } else { + if (signature) { + WebKitDOMNode *parent = webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (signature)); + signature_clone = webkit_dom_node_clone_node_with_error (parent, TRUE, NULL); + remove_node (parent); + } + from = WEBKIT_DOM_NODE (element); + } + + blockquotes_count = create_text_markers_for_citations_in_element (WEBKIT_DOM_ELEMENT (from)); + create_text_markers_for_selection_in_element (WEBKIT_DOM_ELEMENT (from)); + + /* Add the missing BR elements on the end of DIV and P elements to + * preserve the line breaks. But we need to do that just in case that + * there is another element that contains text. */ + list = webkit_dom_element_query_selector_all (WEBKIT_DOM_ELEMENT (from), "div, p", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + gboolean insert = TRUE; + WebKitDOMNode *node, *next_sibling; + + node = webkit_dom_node_list_item (list, ii); + next_sibling = webkit_dom_node_get_next_sibling (node); + + if (!next_sibling) + insert = FALSE; + + while (insert && next_sibling) { + if (!webkit_dom_node_has_child_nodes (next_sibling) && + !webkit_dom_node_get_next_sibling (next_sibling)) + insert = FALSE; + next_sibling = webkit_dom_node_get_next_sibling (next_sibling); + } + + if (insert && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_last_child (node))) + webkit_dom_node_append_child ( + node, + WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), + NULL); + + g_object_unref (node); + } + g_clear_object (&list); + + inner_text = webkit_dom_html_element_get_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (from)); + + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (blockquote), inner_text, NULL); + + inner_html = webkit_dom_element_get_inner_html (blockquote); + + parse_html_into_blocks (editor_page, + main_blockquote ? blockquote : WEBKIT_DOM_ELEMENT (element), + NULL, + inner_html); + + if (main_blockquote) { + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (main_blockquote)), + WEBKIT_DOM_NODE (blockquote), + WEBKIT_DOM_NODE (main_blockquote), + NULL); + + remove_evolution_attributes (WEBKIT_DOM_ELEMENT (element)); + } else { + WebKitDOMNode *first_child; + + if (signature) { + if (!top_signature) { + signature_clone = webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), + signature_clone, + NULL); + } else { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (element), + signature_clone, + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (element)), + NULL); + } + } + + first_child = webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (element)); + if (first_child) { + if (!webkit_dom_node_has_child_nodes (first_child)) { + webkit_dom_element_set_inner_html ( + WEBKIT_DOM_ELEMENT (first_child), + "<br>", + NULL); + } + dom_add_selection_markers_into_element_start ( + document, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL); + } + } + + if (wrap) + *wrap = TRUE; + if (quote) + *quote = main_blockquote || blockquotes_count > 0; + + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (element), "data-converted", "", NULL); + + g_free (inner_text); + g_free (inner_html); +} + +void +e_editor_dom_convert_element_from_html_to_plain_text (EEditorPage *editor_page, + WebKitDOMElement *element) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + convert_element_from_html_to_plain_text (editor_page, element, NULL, NULL); +} + +static void +process_node_to_plain_text_changing_composer_mode (EEditorPage *editor_page, + WebKitDOMNode *source) +{ + WebKitDOMElement *element; + WebKitDOMNamedNodeMap *attributes = NULL; + gint length, ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (source)); + length = webkit_dom_named_node_map_get_length (attributes); + for (ii = 0; ii < length; ii++) { + gchar *name = NULL; + WebKitDOMNode *attribute; + + attribute = webkit_dom_named_node_map_item (attributes, ii); + + name = webkit_dom_node_get_local_name (attribute); + + if (g_strcmp0 (name, "bgcolor") == 0 || + g_strcmp0 (name, "text") == 0 || + g_strcmp0 (name, "vlink") == 0 || + g_strcmp0 (name, "link") == 0) { + + webkit_dom_element_remove_attribute_node ( + WEBKIT_DOM_ELEMENT (source), + WEBKIT_DOM_ATTR (attribute), + NULL); + length--; + } + g_free (name); + g_object_unref (attribute); + } + g_clear_object (&attributes); + + /* Signature */ + element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (source), "div.-x-evo-signature-wrapper", NULL); + if (element) { + WebKitDOMNode *first_child; + + first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)); + + convert_element_from_html_to_plain_text ( + editor_page, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL); + } +} + +/* This function is different than the others there as this needs to go through + * the DOM node by node and generate the plain text of their content. For some + * it will just take the text content, but for example the lists are not that + * easy. */ +static void +process_node_to_plain_text_for_exporting (EEditorPage *editor_page, + WebKitDOMNode *source, + GString *buffer) +{ + WebKitDOMNodeList *nodes = NULL; + gboolean html_mode; + gchar *content = NULL; + gint ii, nodes_length; + + html_mode = e_editor_page_get_html_mode (editor_page); + + nodes = webkit_dom_node_get_child_nodes (source); + nodes_length = webkit_dom_node_list_get_length (nodes); + + for (ii = 0; ii < nodes_length; ii++) { + WebKitDOMNode *child; + gboolean skip_node = FALSE; + + child = webkit_dom_node_list_item (nodes, ii); + + if (WEBKIT_DOM_IS_TEXT (child)) { + gchar *class; + const gchar *css_align = NULL; + GRegex *regex; + + content = webkit_dom_node_get_text_content (child); + if (strstr (content, UNICODE_ZERO_WIDTH_SPACE)) { + gchar *tmp; + + regex = g_regex_new (UNICODE_ZERO_WIDTH_SPACE, 0, 0, NULL); + tmp = g_regex_replace ( + regex, content, -1, 0, "", 0, NULL); + g_free (content); + content = tmp; + g_regex_unref (regex); + } + + class = webkit_dom_element_get_class_name (WEBKIT_DOM_ELEMENT (source)); + if (class && (css_align = strstr (class, "-x-evo-align-"))) { + gchar *content_with_align; + gint word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + + if (!g_str_has_prefix (css_align + 13, "left")) { + gchar *align; + gint length; + + if (g_str_has_prefix (css_align + 13, "center")) + length = (word_wrap_length - g_utf8_strlen (content, -1)) / 2; + else + length = word_wrap_length - g_utf8_strlen (content, -1); + + if (length < 0) + length = 0; + + if (g_str_has_suffix (content, " ")) { + char *tmp; + + length++; + align = g_strnfill (length, ' '); + + tmp = g_strndup (content, g_utf8_strlen (content, -1) -1); + + content_with_align = g_strconcat ( + align, tmp, NULL); + g_free (tmp); + } else { + align = g_strnfill (length, ' '); + + content_with_align = g_strconcat ( + align, content, NULL); + } + + g_free (content); + g_free (align); + content = content_with_align; + } + } + + g_free (class); + + g_string_append (buffer, content); + + goto next; + } + + if (!WEBKIT_DOM_IS_ELEMENT (child)) + goto next; + + if (element_has_class (WEBKIT_DOM_ELEMENT (child), "Apple-tab-span")) { + content = webkit_dom_node_get_text_content (child); + g_string_append (buffer, content); + g_free (content); + skip_node = TRUE; + goto next; + } + + if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child)) + process_quote_nodes (WEBKIT_DOM_ELEMENT (child)); + + if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child) && + element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-indented")) + process_indented_element (WEBKIT_DOM_ELEMENT (child)); + + if (node_is_list (child)) { + process_list_to_plain_text (editor_page, WEBKIT_DOM_ELEMENT (child), 1, buffer); + skip_node = TRUE; + goto next; + } + + if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-resizable-wrapper") && + !element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) { + skip_node = TRUE; + goto next; + } + + /* Signature */ + if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child) && + element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-signature-wrapper")) { + WebKitDOMNode *first_child; + gchar *id; + + first_child = webkit_dom_node_get_first_child (child); + + skip_node = TRUE; + /* Don't generate any text if the signature is set to None. */ + id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child)); + if (g_strcmp0 (id, "none") == 0) { + g_free (id); + + remove_node (child); + goto next; + } + g_free (id); + + if (html_mode) { + convert_element_from_html_to_plain_text ( + editor_page, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL); + skip_node = FALSE; + } + + goto next; + } + + /* Replace smileys with their text representation */ + if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) { + WebKitDOMNode *text_version; + + text_version = webkit_dom_node_get_last_child (child); + content = webkit_dom_html_element_get_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (text_version)); + g_string_append (buffer, content); + g_free (content); + skip_node = TRUE; + goto next; + } + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)) { + g_string_append (buffer, "\n"); + goto next; + } + + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (child)) { + content = webkit_dom_html_element_get_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (child)); + g_string_append (buffer, content); + g_free (content); + skip_node = TRUE; + } + next: + if (!skip_node && webkit_dom_node_has_child_nodes (child)) + process_node_to_plain_text_for_exporting (editor_page, child, buffer); + g_object_unref (child); + } + g_clear_object (&nodes); + + if (!g_str_has_suffix (buffer->str, "\n") && + (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (source) || + WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (source) || + WEBKIT_DOM_IS_HTML_PRE_ELEMENT (source) || + WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (source))) + g_string_append (buffer, "\n"); + + if (g_str_has_suffix (buffer->str, "\n") && + WEBKIT_DOM_IS_HTML_BODY_ELEMENT (source)) + g_string_truncate (buffer, buffer->len - 1); +} + +static void +process_node_to_html_changing_composer_mode (EEditorPage *editor_page, + WebKitDOMNode *source) +{ +} + +static void +process_node_to_html_for_exporting (EEditorPage *editor_page, + WebKitDOMNode *source) +{ + WebKitDOMNodeList *list = NULL; + WebKitDOMHTMLCollection *collection = NULL; + WebKitDOMElement *element; + WebKitDOMDocument *document; + gint ii, length; + + document = webkit_dom_node_get_owner_document (source); + + remove_evolution_attributes (WEBKIT_DOM_ELEMENT (source)); + + /* Aligned elements */ + list = webkit_dom_element_query_selector_all (WEBKIT_DOM_ELEMENT (source), "[class*=\"-x-evo-align\"]", NULL); + length = webkit_dom_node_list_get_length (list); + + for (ii = 0; ii < length; ii++) { + gchar *class = NULL; + WebKitDOMNode *node; + gboolean center = FALSE; + + node = webkit_dom_node_list_item (list, ii); + class = webkit_dom_element_get_class_name (WEBKIT_DOM_ELEMENT (node)); + center = g_strrstr (class, "center") != NULL; + if (center || g_strrstr (class, "right")) { + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node)) + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (node), + "style", + center ? + "list-style-position: inside; text-align: center" : + "list-style-position: inside; text-align: right", + NULL); + else + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (node), + "style", + center ? + "text-align: center" : + "text-align: right", + NULL); + } + element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-left"); + element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-center"); + element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-right"); + g_free (class); + g_object_unref (node); + } + g_clear_object (&list); + + /* Indented elements */ + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), ".-x-evo-indented", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (list, ii); + element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-indented"); + remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node)); + + g_object_unref (node); + } + g_clear_object (&list); + + /* Tab characters */ + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), ".Apple-tab-span", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + gchar *text_content; + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (list, ii); + text_content = webkit_dom_node_get_text_content (node); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (webkit_dom_document_create_text_node (document, text_content)), + node, + NULL); + + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), ".-x-evo-quoted", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *quoted_node; + gchar *text_content; + + quoted_node = webkit_dom_node_list_item (list, ii); + text_content = webkit_dom_node_get_text_content (quoted_node); + webkit_dom_element_set_outer_html ( + WEBKIT_DOM_ELEMENT (quoted_node), text_content, NULL); + + g_free (text_content); + g_object_unref (quoted_node); + } + g_clear_object (&list); + + /* Images */ + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), ".-x-evo-resizable-wrapper:not(.-x-evo-smiley-wrapper)", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node, *image; + + node = webkit_dom_node_list_item (list, ii); + image = webkit_dom_node_get_first_child (node); + + if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (image)) { + remove_evolution_attributes ( + WEBKIT_DOM_ELEMENT (image)); + + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (node), image, node, NULL); + } + + g_object_unref (node); + } + g_clear_object (&list); + + /* Signature */ + element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (source), "div.-x-evo-signature-wrapper", NULL); + if (element) { + WebKitDOMNode *first_child; + gchar *id; + + /* Don't generate any text if the signature is set to None. */ + first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)); + id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child)); + if (g_strcmp0 (id, "none") == 0) { + remove_node (WEBKIT_DOM_NODE (element)); + } else { + remove_base_attributes (element); + remove_base_attributes (WEBKIT_DOM_ELEMENT (first_child)); + remove_evolution_attributes (WEBKIT_DOM_ELEMENT (first_child)); + } + g_free (id); + } + + /* Smileys */ + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), ".-x-evo-smiley-wrapper", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + WebKitDOMElement *img; + + node = webkit_dom_node_list_item (list, ii); + img = WEBKIT_DOM_ELEMENT (webkit_dom_node_get_first_child (node)); + + remove_evolution_attributes (img); + remove_base_attributes (img); + + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (img), + node, + NULL); + + g_object_unref (node); + } + g_clear_object (&list); + + collection = webkit_dom_element_get_elements_by_tag_name_as_html_collection ( + WEBKIT_DOM_ELEMENT (source), "pre"); + length = webkit_dom_html_collection_get_length (collection); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_html_collection_item (collection, ii); + remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node)); + g_object_unref (node); + } + g_clear_object (&collection); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), "p[data-evo-paragraph]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (list, ii); + remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node)); + remove_base_attributes (WEBKIT_DOM_ELEMENT (node)); + g_object_unref (node); + } + g_clear_object (&list); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), "br.-x-evo-wrap-br", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (list, ii); + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "class"); + g_object_unref (node); + } + g_clear_object (&list); +} + +static void +remove_image_attributes_from_element (WebKitDOMElement *element) +{ + webkit_dom_element_remove_attribute (element, "background"); + webkit_dom_element_remove_attribute (element, "data-uri"); + webkit_dom_element_remove_attribute (element, "data-inline"); + webkit_dom_element_remove_attribute (element, "data-name"); +} + +static void +remove_background_images_in_element (WebKitDOMElement *element) +{ + gint length, ii; + WebKitDOMNodeList *images = NULL; + + images = webkit_dom_element_query_selector_all ( + element, "[background][data-inline]", NULL); + + length = webkit_dom_node_list_get_length (images); + for (ii = 0; ii < length; ii++) { + WebKitDOMElement *image = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_list_item (images, ii)); + + remove_image_attributes_from_element (image); + g_object_unref (image); + } + g_clear_object (&images); + + remove_image_attributes_from_element (element); +} + +static void +remove_images_in_element (WebKitDOMElement *element) +{ + gint length, ii; + WebKitDOMNodeList *images = NULL; + + images = webkit_dom_element_query_selector_all ( + element, "img:not(.-x-evo-smiley-img)", NULL); + + length = webkit_dom_node_list_get_length (images); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (images, ii); + remove_node (node); + g_object_unref (node); + } + g_clear_object (&images); +} + +static void +remove_images (WebKitDOMDocument *document) +{ + remove_images_in_element ( + WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document))); +} + +static void +toggle_smileys (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *smileys = NULL; + gboolean html_mode; + gint length; + gint ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + html_mode = e_editor_page_get_html_mode (editor_page); + + smileys = webkit_dom_document_query_selector_all ( + document, "img.-x-evo-smiley-img", NULL); + + length = webkit_dom_node_list_get_length (smileys); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *img = webkit_dom_node_list_item (smileys, ii); + WebKitDOMElement *parent = webkit_dom_node_get_parent_element (img); + + if (html_mode) + element_add_class (parent, "-x-evo-resizable-wrapper"); + else + element_remove_class (parent, "-x-evo-resizable-wrapper"); + g_object_unref (img); + } + g_clear_object (&smileys); +} + +static void +toggle_paragraphs_style_in_element (EEditorPage *editor_page, + WebKitDOMElement *element, + gboolean html_mode) +{ + gint ii, length; + WebKitDOMNodeList *paragraphs = NULL; + + paragraphs = webkit_dom_element_query_selector_all ( + element, ":not(td) > [data-evo-paragraph]", NULL); + + length = webkit_dom_node_list_get_length (paragraphs); + + for (ii = 0; ii < length; ii++) { + gchar *style; + const gchar *css_align; + WebKitDOMNode *node = webkit_dom_node_list_item (paragraphs, ii); + + if (html_mode) { + style = webkit_dom_element_get_attribute ( + WEBKIT_DOM_ELEMENT (node), "style"); + + if (style && (css_align = strstr (style, "text-align: "))) { + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (node), + "style", + g_str_has_prefix (css_align + 12, "center") ? + "text-align: center" : + "text-align: right", + NULL); + } else { + /* In HTML mode the paragraphs don't have width limit */ + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (node), "style"); + } + g_free (style); + } else { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (node); + /* If the paragraph is inside indented paragraph don't set + * the style as it will be inherited */ + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent) && node_is_list (node)) { + gint offset; + + offset = WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node) ? + SPACES_PER_LIST_LEVEL : SPACES_ORDERED_LIST_FIRST_LEVEL; + /* In plain text mode the paragraphs have width limit */ + e_editor_dom_set_paragraph_style ( + editor_page, WEBKIT_DOM_ELEMENT (node), -1, -offset, NULL); + } else if (!element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) { + const gchar *style_to_add = ""; + style = webkit_dom_element_get_attribute ( + WEBKIT_DOM_ELEMENT (node), "style"); + + if (style && (css_align = strstr (style, "text-align: "))) { + style_to_add = g_str_has_prefix ( + css_align + 12, "center") ? + "text-align: center;" : + "text-align: right;"; + } + + /* In plain text mode the paragraphs have width limit */ + e_editor_dom_set_paragraph_style ( + editor_page, WEBKIT_DOM_ELEMENT (node), -1, 0, style_to_add); + + g_free (style); + } + } + g_object_unref (node); + } + g_clear_object (¶graphs); +} + +static void +toggle_paragraphs_style (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + toggle_paragraphs_style_in_element ( + editor_page, + WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)), + e_editor_page_get_html_mode (editor_page)); +} + +gchar * +e_editor_dom_process_content_for_draft (EEditorPage *editor_page, + gboolean only_inner_body) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + WebKitDOMElement *document_element; + WebKitDOMNodeList *list = NULL; + WebKitDOMNode *document_element_clone; + gboolean selection_saved = FALSE; + gchar *content; + gint ii, length; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + body = webkit_dom_document_get_body (document); + + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body), "data-evo-draft", "", NULL); + + if (webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker")) + selection_saved = TRUE; + + if (!selection_saved) + e_editor_dom_selection_save (editor_page); + + document_element = webkit_dom_document_get_document_element (document); + + document_element_clone = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (document_element), TRUE, NULL); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (document_element_clone), "a.-x-evo-visited-link", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *anchor; + + anchor = webkit_dom_node_list_item (list, ii); + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (anchor), "class"); + g_object_unref (anchor); + } + g_clear_object (&list); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (document_element_clone), "#-x-evo-input-start", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (list, ii); + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "id"); + g_object_unref (node); + } + g_clear_object (&list); + + if (only_inner_body) { + WebKitDOMElement *body; + WebKitDOMNode *first_child; + + body = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (document_element_clone), "body", NULL); + + first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + if (!e_editor_page_get_html_mode (editor_page)) + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (first_child), + "data-evo-signature-plain-text-mode", + "", + NULL); + + content = webkit_dom_element_get_inner_html (body); + + if (!e_editor_page_get_html_mode (editor_page)) + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (first_child), + "data-evo-signature-plain-text-mode"); + } else + content = webkit_dom_element_get_outer_html ( + WEBKIT_DOM_ELEMENT (document_element_clone)); + + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (body), "data-evo-draft"); + + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_in_viewport (editor_page); + + if (selection_saved) + e_editor_dom_selection_save (editor_page); + + return content; +} + +static void +toggle_indented_elements (EEditorPage *editor_page) +{ + gboolean html_mode; + gint ii, length; + WebKitDOMDocument *document; + WebKitDOMNodeList *list = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + html_mode = e_editor_page_get_html_mode (editor_page); + list = webkit_dom_document_query_selector_all (document, ".-x-evo-indented", NULL); + length = webkit_dom_node_list_get_length (list); + + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + if (html_mode) + dom_element_swap_attributes (WEBKIT_DOM_ELEMENT (node), "style", "data-plain-text-style"); + else + dom_element_swap_attributes (WEBKIT_DOM_ELEMENT (node), "data-plain-text-style", "style"); + g_object_unref (node); + } + g_clear_object (&list); +} + +static void +process_content_to_html_changing_composer_mode (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNode *body; + WebKitDOMElement *blockquote; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)); + + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text"); + blockquote = webkit_dom_document_query_selector ( + document, "blockquote[type|=cite]", NULL); + + if (blockquote) + dom_dequote_plain_text (document); + + toggle_paragraphs_style (editor_page); + toggle_smileys (editor_page); + remove_images (document); + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (body)); + + process_node_to_html_changing_composer_mode (editor_page, body); +} + +static void +wrap_paragraphs_in_quoted_content (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *paragraphs = NULL; + gint ii, length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + paragraphs = webkit_dom_document_query_selector_all ( + document, "blockquote[type=cite] > [data-evo-paragraph]", NULL); + + length = webkit_dom_node_list_get_length (paragraphs); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *paragraph; + + paragraph = webkit_dom_node_list_item (paragraphs, ii); + + e_editor_dom_wrap_paragraph (editor_page, WEBKIT_DOM_ELEMENT (paragraph)); + + g_object_unref (paragraph); + } + g_clear_object (¶graphs); +} + +static void +process_content_to_plain_text_changing_composer_mode (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNode *body, *head, *node; + WebKitDOMElement *blockquote; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)); + head = WEBKIT_DOM_NODE (webkit_dom_document_get_head (document)); + + while ((node = webkit_dom_node_get_last_child (head))) + remove_node (node); + + e_editor_dom_selection_save (editor_page); + + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (body), "data-user-colors"); + + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text", "", NULL); + + blockquote = webkit_dom_document_query_selector ( + document, "blockquote[type|=cite]", NULL); + + if (blockquote) { + wrap_paragraphs_in_quoted_content (editor_page); + quote_plain_text_elements_after_wrapping_in_document (editor_page); + } + + toggle_paragraphs_style (editor_page); + toggle_smileys (editor_page); + toggle_indented_elements (editor_page); + remove_images (document); + remove_background_images_in_element (WEBKIT_DOM_ELEMENT (body)); + + process_node_to_plain_text_changing_composer_mode (editor_page, body); + + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_in_viewport (editor_page); +} + +gchar * +e_editor_dom_process_content_to_plain_text_for_exporting (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNode *body, *source; + WebKitDOMNodeList *list = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + gboolean wrap = FALSE, quote = FALSE, remove_last_new_line = FALSE; + gint length, ii; + GString *plain_text; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + plain_text = g_string_sized_new (1024); + + body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)); + source = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), TRUE, NULL); + + e_editor_dom_selection_save (editor_page); + + /* If composer is in HTML mode we have to move the content to plain version */ + if (e_editor_page_get_html_mode (editor_page)) { + if (e_editor_dom_check_if_conversion_needed (editor_page)) { + WebKitDOMElement *wrapper; + WebKitDOMNode *child; + + wrapper = webkit_dom_document_create_element (document, "div", NULL); + webkit_dom_element_set_id (wrapper, "-x-evo-html-to-plain-text-wrapper"); + while ((child = webkit_dom_node_get_first_child (source))) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (wrapper), + child, + NULL); + } + + list = webkit_dom_element_query_selector_all ( + wrapper, "#-x-evo-input-start", NULL); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *paragraph; + + paragraph = webkit_dom_node_list_item (list, ii); + + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (paragraph), "id"); + } + g_clear_object (&list); + + remove_images_in_element (wrapper); + + list = webkit_dom_element_query_selector_all ( + wrapper, "#-x-evo-html-to-plain-text-wrapper > :matches(ul, ol)", NULL); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMElement *list_pre; + WebKitDOMNode *item; + GString *list_plain_text; + + item = webkit_dom_node_list_item (list, ii); + + list_plain_text = g_string_new (""); + + process_list_to_plain_text ( + editor_page, WEBKIT_DOM_ELEMENT (item), 1, list_plain_text); + + list_pre = webkit_dom_document_create_element (document, "pre", NULL); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (list_pre), + g_string_free (list_plain_text, FALSE), + NULL); + webkit_dom_node_replace_child ( + WEBKIT_DOM_NODE (wrapper), + WEBKIT_DOM_NODE (list_pre), + item, + NULL); + g_object_unref (item); + } + g_clear_object (&list); + + convert_element_from_html_to_plain_text ( + editor_page, wrapper, &wrap, "e); + + source = WEBKIT_DOM_NODE (wrapper); + + remove_last_new_line = TRUE; + } else { + toggle_paragraphs_style_in_element ( + editor_page, WEBKIT_DOM_ELEMENT (source), FALSE); + remove_images_in_element ( + WEBKIT_DOM_ELEMENT (source)); + remove_background_images_in_element ( + WEBKIT_DOM_ELEMENT (source)); + } + } + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), "[data-evo-paragraph]", NULL); + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); + g_clear_object (&dom_window); + g_clear_object (&dom_selection); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *paragraph; + + paragraph = webkit_dom_node_list_item (list, ii); + + if (node_is_list (paragraph)) { + WebKitDOMNode *item = webkit_dom_node_get_first_child (paragraph); + + while (item) { + WebKitDOMNode *next_item = + webkit_dom_node_get_next_sibling (item); + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) + e_editor_dom_wrap_paragraph (editor_page, WEBKIT_DOM_ELEMENT (item)); + + item = next_item; + } + } else if (!webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (paragraph), ".-x-evo-wrap-br,.-x-evo-quoted", NULL)) { + /* Dont't try to wrap the already wrapped content. */ + e_editor_dom_wrap_paragraph (editor_page, WEBKIT_DOM_ELEMENT (paragraph)); + } + g_object_unref (paragraph); + } + g_clear_object (&list); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (source), "#-x-evo-selection-start-marker, #-x-evo-selection-end-marker", NULL); + + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); + + remove_node (node); + g_object_unref (node); + webkit_dom_node_normalize (parent); + } + g_clear_object (&list); + + if (quote) + quote_plain_text_recursive (document, source, source, 0); + else if (e_editor_page_get_html_mode (editor_page)) { + WebKitDOMElement *citation; + + citation = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (source), "blockquote[type=cite]", NULL); + if (citation) + quote_plain_text_recursive (document, source, source, 0); + } + + process_node_to_plain_text_for_exporting (editor_page, source, plain_text); + /* Truncate the extra new line on the end of generated text as the + * check inside the previous function is based on whether the processed + * node is BODY or not, but in this case the content is wrapped in DIV. */ + if (remove_last_new_line) + g_string_truncate (plain_text, plain_text->len - 1); + + e_editor_dom_selection_restore (editor_page); + + /* Return text content between <body> and </body> */ + return g_string_free (plain_text, FALSE); +} + +static void +restore_image (WebKitDOMDocument *document, + const gchar *id, + const gchar *element_src) +{ + gchar *selector; + gint length, ii; + WebKitDOMNodeList *list = NULL; + + selector = g_strconcat ("[data-inline][background=\"cid:", id, "\"]", NULL); + list = webkit_dom_document_query_selector_all (document, selector, NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMElement *element = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_list_item (list, ii)); + + webkit_dom_element_set_attribute (element, "background", element_src, NULL); + g_object_unref (element); + } + g_free (selector); + g_clear_object (&list); + + selector = g_strconcat ("[data-inline][src=\"cid:", id, "\"]", NULL); + list = webkit_dom_document_query_selector_all (document, selector, NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMElement *element = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_list_item (list, ii)); + + webkit_dom_element_set_attribute (element, "src", element_src, NULL); + g_object_unref (element); + } + g_free (selector); + g_clear_object (&list); +} + +void +e_editor_dom_restore_images (EEditorPage *editor_page, + GVariant *inline_images_to_restore) +{ + WebKitDOMDocument *document; + const gchar *element_src, *name, *id; + GVariantIter *iter; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + g_variant_get (inline_images_to_restore, "a(sss)", &iter); + while (g_variant_iter_loop (iter, "(&s&s&s)", &element_src, &name, &id)) + restore_image (document, id, element_src); + + g_variant_iter_free (iter); +} + +gchar * +e_editor_dom_process_content_to_html_for_exporting (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + WebKitDOMNode *node, *document_clone; + WebKitDOMNodeList *list = NULL; + GSettings *settings; + gint ii, length; + gchar *html_content; + gboolean send_editor_colors = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + + document_clone = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (webkit_dom_document_get_document_element (document)), TRUE, NULL); + element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-quote-style", NULL); + if (element) + remove_node (WEBKIT_DOM_NODE (element)); + element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-a-color-style", NULL); + if (element) + remove_node (WEBKIT_DOM_NODE (element)); + element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-a-color-style-visited", NULL); + if (element) + remove_node (WEBKIT_DOM_NODE (element)); + /* When the Ctrl + Enter is pressed for sending, the links are activated. */ + element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-style-a", NULL); + if (element) + remove_node (WEBKIT_DOM_NODE (element)); + node = WEBKIT_DOM_NODE (webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (document_clone), "body", NULL)); + element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (node), "#-x-evo-selection-start-marker", NULL); + if (element) + remove_node (WEBKIT_DOM_NODE (element)); + element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (node), "#-x-evo-selection-end-marker", NULL); + if (element) + remove_node (WEBKIT_DOM_NODE (element)); + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + send_editor_colors = g_settings_get_boolean (settings, "composer-inherit-theme-colors"); + g_object_unref (settings); + + if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), "data-user-colors")) { + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "data-user-colors"); + } else if (!send_editor_colors) { + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "bgcolor"); + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "text"); + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "link"); + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "vlink"); + } + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (node), "span[data-hidden-space]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *hidden_space_node; + + hidden_space_node = webkit_dom_node_list_item (list, ii); + remove_node (hidden_space_node); + g_object_unref (hidden_space_node); + } + g_clear_object (&list); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (node), "[data-style]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *data_style_node; + + data_style_node = webkit_dom_node_list_item (list, ii); + + element_rename_attribute (WEBKIT_DOM_ELEMENT (data_style_node), "data-style", "style"); + g_object_unref (data_style_node); + } + g_clear_object (&list); + + process_node_to_html_for_exporting (editor_page, node); + + html_content = webkit_dom_element_get_outer_html ( + WEBKIT_DOM_ELEMENT (document_clone)); + + g_object_unref (document_clone); + + return html_content; +} + +void +e_editor_dom_convert_when_changing_composer_mode (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + gboolean quote = FALSE, wrap = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + body = webkit_dom_document_get_body (document); + + convert_element_from_html_to_plain_text ( + editor_page, WEBKIT_DOM_ELEMENT (body), &wrap, "e); + + if (wrap) + e_editor_dom_wrap_paragraphs_in_document (editor_page); + + if (quote) { + e_editor_dom_selection_save (editor_page); + if (wrap) + quote_plain_text_elements_after_wrapping_in_document (editor_page); + else + body = WEBKIT_DOM_HTML_ELEMENT (dom_quote_plain_text (document)); + e_editor_dom_selection_restore (editor_page); + } + + toggle_paragraphs_style (editor_page); + toggle_smileys (editor_page); + remove_images (document); + remove_background_images_in_element (WEBKIT_DOM_ELEMENT (body)); + + clear_attributes (editor_page); + + if (!e_editor_page_get_html_mode (editor_page)) + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text", "", NULL); + else + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text"); + + e_editor_dom_force_spell_check_in_viewport (editor_page); + e_editor_dom_scroll_to_caret (editor_page); +} + +static void +set_base64_to_element_attribute (GHashTable *inline_images, + WebKitDOMElement *element, + const gchar *attribute) +{ + gchar *attribute_value; + const gchar *base64_src; + + attribute_value = webkit_dom_element_get_attribute (element, attribute); + + if (attribute_value && (base64_src = g_hash_table_lookup (inline_images, attribute_value)) != NULL) { + const gchar *base64_data = strstr (base64_src, ";") + 1; + gchar *name; + glong name_length; + + name_length = + g_utf8_strlen (base64_src, -1) - + g_utf8_strlen (base64_data, -1) - 1; + name = g_strndup (base64_src, name_length); + + webkit_dom_element_set_attribute (element, "data-inline", "", NULL); + webkit_dom_element_set_attribute (element, "data-name", name, NULL); + webkit_dom_element_set_attribute (element, attribute, base64_data, NULL); + + g_free (name); + } + g_free (attribute_value); +} + +static void +change_cid_images_src_to_base64 (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *document_element; + WebKitDOMNamedNodeMap *attributes = NULL; + WebKitDOMNodeList *list = NULL; + GHashTable *inline_images; + gint ii, length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + inline_images = e_editor_page_get_inline_images (editor_page); + + document_element = webkit_dom_document_get_document_element (document); + + list = webkit_dom_document_query_selector_all (document, "img[src^=\"cid:\"]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + set_base64_to_element_attribute (inline_images, WEBKIT_DOM_ELEMENT (node), "src"); + g_object_unref (node); + } + g_clear_object (&list); + + /* Namespaces */ + attributes = webkit_dom_element_get_attributes (document_element); + length = webkit_dom_named_node_map_get_length (attributes); + for (ii = 0; ii < length; ii++) { + gchar *name; + WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); + + name = webkit_dom_node_get_local_name (node); + + if (g_str_has_prefix (name, "xmlns:")) { + const gchar *ns = name + 6; + gchar *attribute_ns = g_strconcat (ns, ":src", NULL); + gchar *selector = g_strconcat ("img[", ns, "\\:src^=\"cid:\"]", NULL); + gint ns_length, jj; + + list = webkit_dom_document_query_selector_all ( + document, selector, NULL); + ns_length = webkit_dom_node_list_get_length (list); + for (jj = 0; jj < ns_length; jj++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, jj); + + set_base64_to_element_attribute ( + inline_images, WEBKIT_DOM_ELEMENT (node), attribute_ns); + g_object_unref (node); + } + + g_clear_object (&list); + g_free (attribute_ns); + g_free (selector); + } + g_object_unref (node); + g_free (name); + } + g_clear_object (&attributes); + + list = webkit_dom_document_query_selector_all ( + document, "[background^=\"cid:\"]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + set_base64_to_element_attribute ( + inline_images, WEBKIT_DOM_ELEMENT (node), "background"); + g_object_unref (node); + } + g_clear_object (&list); +} + +static void +adapt_to_editor_dom_changes (WebKitDOMDocument *document) +{ + WebKitDOMHTMLCollection *collection = NULL; + gint ii, length; + + /* Normal block code div.-x-evo-paragraph replaced by p[data-evo-paragraph] */ + collection = webkit_dom_document_get_elements_by_class_name_as_html_collection (document, "-x-evo-paragraph"); + length = webkit_dom_html_collection_get_length (collection); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node, *child; + WebKitDOMElement *element; + gchar *style; + + node = webkit_dom_html_collection_item (collection, ii); + element = webkit_dom_document_create_element (document, "p", NULL); + webkit_dom_element_set_attribute (element, "data-evo-paragraph", "", NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + node, + NULL); + + while ((child = webkit_dom_node_get_first_child (node))) + webkit_dom_node_append_child (WEBKIT_DOM_NODE (element), child, NULL); + + style = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "style"); + if (style) + webkit_dom_element_set_attribute (element, "style", style, NULL); + + remove_node (node); + g_object_unref (node); + } + g_clear_object (&collection); +} + +void +e_editor_dom_process_content_after_load (EEditorPage *editor_page) +{ + gboolean html_mode; + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + WebKitDOMDOMWindow *dom_window = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + /* Don't use CSS when possible to preserve compatibility with older + * versions of Evolution or other MUAs */ + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS, "false"); + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, "p"); + + body = webkit_dom_document_get_body (document); + + webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (body), "style"); + html_mode = e_editor_page_get_html_mode (editor_page); + if (!html_mode) + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text", "", NULL); + + if (e_editor_page_get_convert_in_situ (editor_page)) { + e_editor_dom_convert_content (editor_page, NULL); + /* Make the quote marks non-selectable. */ + e_editor_dom_disable_quote_marks_select (editor_page); + dom_set_links_active (document, FALSE); + e_editor_page_set_convert_in_situ (editor_page, FALSE); + + e_editor_dom_register_input_event_listener_on_body (editor_page); + register_html_events_handlers (editor_page, body); + + return; + } + + adapt_to_editor_dom_changes (document); + + /* Make the quote marks non-selectable. */ + e_editor_dom_disable_quote_marks_select (editor_page); + dom_set_links_active (document, FALSE); + put_body_in_citation (document); + move_elements_to_body (editor_page); + repair_gmail_blockquotes (document); + remove_thunderbird_signature (document); + + if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (body), "data-evo-draft")) { + /* Restore the selection how it was when the draft was saved */ + e_editor_dom_move_caret_into_element (editor_page, WEBKIT_DOM_ELEMENT (body), FALSE); + e_editor_dom_selection_restore (editor_page); + e_editor_dom_remove_embedded_style_sheet (editor_page); + } + + /* The composer body could be empty in some case (loading an empty string + * or empty HTML. In that case create the initial paragraph. */ + if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))) { + WebKitDOMElement *paragraph; + + paragraph = e_editor_dom_prepare_paragraph (editor_page, TRUE); + webkit_dom_element_set_id (paragraph, "-x-evo-input-start"); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (paragraph), NULL); + e_editor_dom_selection_restore (editor_page); + } + + /* Register on input event that is called when the content (body) is modified */ + e_editor_dom_register_input_event_listener_on_body (editor_page); + register_html_events_handlers (editor_page, body); + + e_editor_dom_fix_file_uri_images (editor_page); + change_cid_images_src_to_base64 (editor_page); + + if (e_editor_page_get_inline_spelling_enabled (editor_page)) + e_editor_dom_force_spell_check (editor_page); + else + e_editor_dom_turn_spell_check_off (editor_page); + + e_editor_dom_set_monospace_font_family_on_body ( + WEBKIT_DOM_ELEMENT (body), e_editor_page_get_html_mode (editor_page)); + + dom_window = webkit_dom_document_get_default_view (document); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (dom_window), + "scroll", + G_CALLBACK (body_scroll_event_cb), + FALSE, + editor_page); + + /* Intentionally leak the WebKitDOMDOMWindow object here as otherwise the + * callback won't be set up. */ +} + +GVariant * +e_editor_dom_get_inline_images_data (EEditorPage *editor_page, + const gchar *uid_domain) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *list = NULL; + GVariant *result = NULL; + GVariantBuilder *builder = NULL; + GHashTable *added = NULL; + gint length, ii; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + list = webkit_dom_document_query_selector_all (document, "img[data-inline]", NULL); + + length = webkit_dom_node_list_get_length (list); + if (length == 0) { + g_clear_object (&list); + goto background; + } + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a(sss)")); + + added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + for (ii = 0; ii < length; ii++) { + const gchar *id; + gchar *cid = NULL; + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + gchar *src = webkit_dom_element_get_attribute ( + WEBKIT_DOM_ELEMENT (node), "src"); + + if (!src) + continue; + + if ((id = g_hash_table_lookup (added, src)) != NULL) { + cid = g_strdup_printf ("cid:%s", id); + g_free (src); + } else { + gchar *data_name = webkit_dom_element_get_attribute ( + WEBKIT_DOM_ELEMENT (node), "data-name"); + + if (data_name) { + gchar *new_id; + + new_id = camel_header_msgid_generate (uid_domain); + g_variant_builder_add ( + builder, "(sss)", src, data_name, new_id); + cid = g_strdup_printf ("cid:%s", new_id); + + g_hash_table_insert (added, src, new_id); + } + g_free (data_name); + } + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (node), "src", cid, NULL); + g_object_unref (node); + g_free (cid); + } + g_clear_object (&list); + + background: + list = webkit_dom_document_query_selector_all ( + document, "[data-inline][background]", NULL); + length = webkit_dom_node_list_get_length (list); + if (length == 0) + goto out; + if (!builder) + builder = g_variant_builder_new (G_VARIANT_TYPE ("a(sss)")); + if (!added) + added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + for (ii = 0; ii < length; ii++) { + const gchar *id; + gchar *cid = NULL; + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + gchar *src = webkit_dom_element_get_attribute ( + WEBKIT_DOM_ELEMENT (node), "background"); + + if (!src) + continue; + + if ((id = g_hash_table_lookup (added, src)) != NULL) { + cid = g_strdup_printf ("cid:%s", id); + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (node), "background", cid, NULL); + g_free (src); + } else { + gchar *data_name = webkit_dom_element_get_attribute ( + WEBKIT_DOM_ELEMENT (node), "data-name"); + + if (data_name) { + gchar *new_id; + + new_id = camel_header_msgid_generate (uid_domain); + g_variant_builder_add ( + builder, "(sss)", src, data_name, new_id); + cid = g_strdup_printf ("cid:%s", new_id); + + g_hash_table_insert (added, src, new_id); + + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (node), "background", cid, NULL); + } + g_free (data_name); + } + g_free (cid); + g_object_unref (node); + } + out: + g_clear_object (&list); + if (added) + g_hash_table_destroy (added); + + if (builder) { + result = g_variant_new ("a(sss)", builder); + g_variant_builder_unref (builder); + } + + return result; +} + +static gboolean +pasting_quoted_content (const gchar *content) +{ + /* Check if the content we are pasting is a quoted content from composer. + * If it is, we can't use WebKit to paste it as it would leave the formatting + * on the content. */ + return g_str_has_prefix ( + content, + "<meta http-equiv=\"content-type\" content=\"text/html; " + "charset=utf-8\"><blockquote type=\"cite\"") && + strstr (content, "\"-x-evo-"); +} + +/* + * e_editor_dom_insert_html: + * @selection: an #EEditorSelection + * @html_text: an HTML code to insert + * + * Insert @html_text into document at current cursor position. When a text range + * is selected, it will be replaced by @html_text. + */ +void +e_editor_dom_insert_html (EEditorPage *editor_page, + const gchar *html_text) +{ + WebKitDOMDocument *document; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + gboolean html_mode; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + g_return_if_fail (html_text != NULL); + + document = e_editor_page_get_document (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + gboolean collapsed; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INSERT_HTML; + + collapsed = e_editor_dom_selection_is_collapsed (editor_page); + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + if (!collapsed) { + ev->before.end.x = ev->before.start.x; + ev->before.end.y = ev->before.start.y; + } + + ev->data.string.from = NULL; + ev->data.string.to = g_strdup (html_text); + } + + html_mode = e_editor_page_get_html_mode (editor_page); + if (html_mode || + (e_editor_page_is_pasting_content_from_itself (editor_page) && + !pasting_quoted_content (html_text))) { + if (!e_editor_dom_selection_is_collapsed (editor_page)) { + EEditorHistoryEvent *event; + WebKitDOMDocumentFragment *fragment; + WebKitDOMRange *range = NULL; + + event = g_new0 (EEditorHistoryEvent, 1); + event->type = HISTORY_DELETE; + + range = e_editor_dom_get_current_range (editor_page); + fragment = webkit_dom_range_clone_contents (range, NULL); + g_clear_object (&range); + event->data.fragment = fragment; + + e_editor_dom_selection_get_coordinates (editor_page, + &event->before.start.x, + &event->before.start.y, + &event->before.end.x, + &event->before.end.y); + + event->after.start.x = event->before.start.x; + event->after.start.y = event->before.start.y; + event->after.end.x = event->before.start.x; + event->after.end.y = event->before.start.y; + + e_editor_undo_redo_manager_insert_history_event (manager, event); + + event = g_new0 (EEditorHistoryEvent, 1); + event->type = HISTORY_AND; + + e_editor_undo_redo_manager_insert_history_event (manager, event); + } + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_HTML, html_text); + e_editor_dom_fix_file_uri_images (editor_page); + if (strstr (html_text, "id=\"-x-evo-selection-start-marker\"")) + e_editor_dom_selection_restore (editor_page); + + if (!html_mode) { + WebKitDOMNodeList *list = NULL; + gint ii, length; + + list = webkit_dom_document_query_selector_all ( + document, "span[style^=font-family]", NULL); + length = webkit_dom_node_list_get_length (list); + if (length > 0) + e_editor_dom_selection_save (editor_page); + + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *span, *child; + + span = webkit_dom_node_list_item (list, ii); + while ((child = webkit_dom_node_get_first_child (span))) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (span), + child, + span, + NULL); + + remove_node (span); + g_object_unref (span); + } + g_clear_object (&list); + + if (length > 0) + e_editor_dom_selection_restore (editor_page); + } + + e_editor_dom_check_magic_links (editor_page, FALSE); + e_editor_dom_force_spell_check (editor_page); + e_editor_dom_scroll_to_caret (editor_page); + } else + e_editor_dom_convert_and_insert_html_into_selection (editor_page, html_text, TRUE); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } +} + +static void +save_history_for_delete_or_backspace (EEditorPage *editor_page, + gboolean delete_key, + gboolean control_key) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (!webkit_dom_dom_selection_get_range_count (dom_selection)) { + g_clear_object (&dom_selection); + return; + } + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + /* Check if we can delete something */ + if (webkit_dom_range_get_collapsed (range, NULL)) { + WebKitDOMRange *tmp_range = NULL; + + webkit_dom_dom_selection_modify ( + dom_selection, "move", delete_key ? "right" : "left", "character"); + + tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + if (webkit_dom_range_compare_boundary_points (tmp_range, WEBKIT_DOM_RANGE_END_TO_END, range, NULL) == 0) { + g_clear_object (&dom_selection); + g_clear_object (&range); + g_clear_object (&tmp_range); + + return; + } + + webkit_dom_dom_selection_modify ( + dom_selection, "move", delete_key ? "left" : "right", "character"); + + g_clear_object (&tmp_range); + } + + if (save_history_before_event_in_table (editor_page, range)) { + g_clear_object (&range); + g_clear_object (&dom_selection); + return; + } + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_DELETE; + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + g_clear_object (&range); + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + if (webkit_dom_range_get_collapsed (range, NULL)) { + gboolean removing_from_anchor = FALSE; + WebKitDOMRange *range_clone = NULL; + WebKitDOMNode *node, *next_block = NULL; + + e_editor_page_block_selection_changed (editor_page); + + range_clone = webkit_dom_range_clone_range (range, NULL); + if (control_key) { + WebKitDOMRange *tmp_range = NULL; + + /* Control + Delete/Backspace deletes previous/next word. */ + webkit_dom_dom_selection_modify ( + dom_selection, "move", delete_key ? "right" : "left", "word"); + tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + if (delete_key) + webkit_dom_range_set_end ( + range_clone, + webkit_dom_range_get_end_container (tmp_range, NULL), + webkit_dom_range_get_end_offset (tmp_range, NULL), + NULL); + else + webkit_dom_range_set_start ( + range_clone, + webkit_dom_range_get_start_container (tmp_range, NULL), + webkit_dom_range_get_start_offset (tmp_range, NULL), + NULL); + g_clear_object (&tmp_range); + } else { + typedef WebKitDOMNode * (*GetSibling)(WebKitDOMNode *node); + WebKitDOMNode *container, *sibling; + WebKitDOMElement *selection_marker; + + GetSibling get_sibling = delete_key ? + webkit_dom_node_get_next_sibling : + webkit_dom_node_get_previous_sibling; + + container = webkit_dom_range_get_end_container (range_clone, NULL); + sibling = get_sibling (container); + + selection_marker = webkit_dom_document_get_element_by_id ( + document, + delete_key ? + "-x-evo-selection-end-marker" : + "-x-evo-selection-start-marker"); + + if (selection_marker) { + WebKitDOMNode *tmp_sibling; + + tmp_sibling = get_sibling (WEBKIT_DOM_NODE (selection_marker)); + if (!tmp_sibling || (WEBKIT_DOM_IS_HTML_BR_ELEMENT (tmp_sibling) && + !element_has_class (WEBKIT_DOM_ELEMENT (tmp_sibling), "-x-evo-wrap-br"))) + sibling = WEBKIT_DOM_NODE (selection_marker); + } + + if (e_editor_dom_is_selection_position_node (sibling)) { + if ((node = get_sibling (sibling))) + node = get_sibling (node); + if (node) { + if (WEBKIT_DOM_IS_ELEMENT (node) && + webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), "data-hidden-space")) { + fragment = webkit_dom_document_create_document_fragment (document); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + webkit_dom_document_create_text_node (document, " ")), + NULL); + } else if (delete_key) { + webkit_dom_range_set_start ( + range_clone, node, 0, NULL); + webkit_dom_range_set_end ( + range_clone, node, 1, NULL); + } + } else { + WebKitDOMRange *tmp_range = NULL, *actual_range = NULL; + + actual_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + webkit_dom_dom_selection_modify ( + dom_selection, "move", delete_key ? "right" : "left", "character"); + + tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + if (webkit_dom_range_compare_boundary_points (tmp_range, WEBKIT_DOM_RANGE_END_TO_END, actual_range, NULL) != 0) { + WebKitDOMNode *actual_block; + WebKitDOMNode *tmp_block; + + actual_block = e_editor_dom_get_parent_block_node_from_child (container); + + tmp_block = delete_key ? + webkit_dom_range_get_end_container (tmp_range, NULL) : + webkit_dom_range_get_start_container (tmp_range, NULL); + tmp_block = e_editor_dom_get_parent_block_node_from_child (tmp_block); + + webkit_dom_dom_selection_modify ( + dom_selection, "move", delete_key ? "left" : "right", "character"); + + if (tmp_block) { + fragment = webkit_dom_document_create_document_fragment (document); + if (delete_key) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (actual_block, TRUE, NULL), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (tmp_block, TRUE, NULL), + NULL); + if (delete_key) + next_block = tmp_block; + } else { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (tmp_block, TRUE, NULL), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (actual_block, TRUE, NULL), + NULL); + } + g_object_set_data ( + G_OBJECT (fragment), + "history-concatenating-blocks", + GINT_TO_POINTER (1)); + } + } + g_clear_object (&tmp_range); + g_clear_object (&actual_range); + } + } else { + glong offset; + + /* FIXME This code is wrong for unicode smileys. */ + offset = webkit_dom_range_get_start_offset (range_clone, NULL); + + if (delete_key) + webkit_dom_range_set_end ( + range_clone, container, offset + 1, NULL); + else + webkit_dom_range_set_start ( + range_clone, container, offset - 1, NULL); + + removing_from_anchor = WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT ( + webkit_dom_node_get_parent_node (container)); + } + } + + + if (!fragment) + fragment = webkit_dom_range_clone_contents (range_clone, NULL); + if (removing_from_anchor) + g_object_set_data ( + G_OBJECT (fragment), + "history-removing-from-anchor", + GINT_TO_POINTER (1)); + node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + if (!node) { + g_free (ev); + e_editor_page_unblock_selection_changed (editor_page); + g_clear_object (&range); + g_clear_object (&range_clone); + g_clear_object (&dom_selection); + g_warning ("History event was not saved for %s key", delete_key ? "Delete" : "Backspace"); + return; + } + + if (control_key) { + if (delete_key) { + ev->after.start.x = ev->before.start.x; + ev->after.start.y = ev->before.start.y; + ev->after.end.x = ev->before.end.x; + ev->after.end.y = ev->before.end.y; + + webkit_dom_range_collapse (range_clone, TRUE, NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range_clone); + } else { + gboolean selection_saved = FALSE; + WebKitDOMRange *tmp_range = NULL; + + if (webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker")) + selection_saved = TRUE; + + if (selection_saved) + e_editor_dom_selection_restore (editor_page); + + tmp_range = webkit_dom_range_clone_range (range_clone, NULL); + /* Prepare the selection to the right position after + * delete and save it. */ + webkit_dom_range_collapse (range_clone, TRUE, NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range_clone); + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); + /* Restore the selection where it was before the + * history event was saved. */ + webkit_dom_range_collapse (tmp_range, FALSE, NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, tmp_range); + g_clear_object (&tmp_range); + + if (selection_saved) + e_editor_dom_selection_save (editor_page); + } + } else { + gboolean selection_saved = FALSE; + + if (webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker")) + selection_saved = TRUE; + + if (selection_saved) + e_editor_dom_selection_restore (editor_page); + + if (delete_key) { + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); + } else { + webkit_dom_dom_selection_modify (dom_selection, "move", "left", "character"); + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); + webkit_dom_dom_selection_modify (dom_selection, "move", "right", "character"); + + ev->after.end.x = ev->after.start.x; + ev->after.end.y = ev->after.start.y; + } + + if (selection_saved) + e_editor_dom_selection_save (editor_page); + } + + g_clear_object (&range_clone); + + if (delete_key) { + if (!WEBKIT_DOM_IS_ELEMENT (node)) { + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, FALSE)), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), + NULL); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, TRUE)), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), + NULL); + } + } else { + if (!WEBKIT_DOM_IS_ELEMENT (node)) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, TRUE)), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, FALSE)), + NULL); + } + } + + /* If concatenating two blocks with pressing Delete on the end + * of the previous one and the next node contain content that + * is wrapped on multiple lines, the last line will by separated + * by WebKit to the separate block. To avoid it let's remove + * all quoting and wrapping from the next paragraph. */ + if (next_block) { + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (next_block)); + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (next_block)); + } + + e_editor_page_unblock_selection_changed (editor_page); + } else { + WebKitDOMElement *tmp_element; + WebKitDOMNode *sibling; + + ev->after.start.x = ev->before.start.x; + ev->after.start.y = ev->before.start.y; + ev->after.end.x = ev->before.start.x; + ev->after.end.y = ev->before.start.y; + + fragment = webkit_dom_range_clone_contents (range, NULL); + + tmp_element = webkit_dom_document_fragment_query_selector ( + fragment, "#-x-evo-selection-start-marker", NULL); + if (tmp_element) + remove_node (WEBKIT_DOM_NODE (tmp_element)); + + tmp_element = webkit_dom_document_fragment_query_selector ( + fragment, "#-x-evo-selection-end-marker", NULL); + if (tmp_element) + remove_node (WEBKIT_DOM_NODE (tmp_element)); + + /* If any empty blockquote is presented, remove it. */ + tmp_element = webkit_dom_document_query_selector ( + document, "blockquote[type=cite]:empty", NULL); + if (tmp_element) + remove_node (WEBKIT_DOM_NODE (tmp_element)); + + /* Selection starts in the beginning of blockquote. */ + tmp_element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element)); + if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-quoted")) { + WebKitDOMNode *child; + + tmp_element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + /* If there is no text after the selection end it means that + * the block will be replaced with block that is body's descendant + * and not the blockquote's one. Also if the selection started + * in the beginning of blockquote we have to insert the quote + * characters into the deleted content to correctly restore + * them during undo/redo operations. */ + if (!(tmp_element && webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element)))) { + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + while (child && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child)) + child = webkit_dom_node_get_first_child (child); + + child = webkit_dom_node_get_first_child (child); + if (child && (WEBKIT_DOM_IS_TEXT (child) || + (WEBKIT_DOM_IS_ELEMENT (child) && + !element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-quoted")))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (child), + webkit_dom_node_clone_node_with_error (sibling, TRUE, NULL), + child, + NULL); + } + } + } + + /* When we were cloning the range above and the range contained + * quoted content there will still be blockquote missing in the + * final range. Let's modify the fragment and add it there. */ + tmp_element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + if (tmp_element) { + WebKitDOMNode *node; + + node = WEBKIT_DOM_NODE (tmp_element); + while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node))) + node = webkit_dom_node_get_parent_node (node); + + if (node && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) { + WebKitDOMNode *last_child; + + last_child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)); + + if (last_child && !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child)) { + WebKitDOMDocumentFragment *tmp_fragment; + WebKitDOMNode *clone; + + tmp_fragment = webkit_dom_document_create_document_fragment (document); + clone = webkit_dom_node_clone_node_with_error (node, FALSE, NULL); + clone = webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (tmp_fragment), clone, NULL); + webkit_dom_node_append_child (clone, WEBKIT_DOM_NODE (fragment), NULL); + fragment = tmp_fragment; + } + } + } + + /* FIXME Ugly hack */ + /* If the deleted selection contained the signature (or at least its + * part) replace it with the unchanged signature to correctly perform + * undo operation. */ + tmp_element = webkit_dom_document_fragment_query_selector (fragment, ".-x-evo-signature-wrapper", NULL); + if (tmp_element) { + WebKitDOMElement *signature; + + signature = webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL); + if (signature) { + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp_element)), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (signature), TRUE, NULL), + WEBKIT_DOM_NODE (tmp_element), + NULL); + } + } + } + + g_clear_object (&range); + g_clear_object (&dom_selection); + + g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (delete_key)); + g_object_set_data (G_OBJECT (fragment), "history-control-key", GINT_TO_POINTER (control_key)); + + ev->data.fragment = fragment; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + e_editor_undo_redo_manager_insert_history_event (manager, ev); +} + +gboolean +e_editor_dom_fix_structure_after_delete_before_quoted_content (EEditorPage *editor_page, + glong key_code, + gboolean control_key, + gboolean delete_key) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *block, *node; + gboolean collapsed = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + collapsed = e_editor_dom_selection_is_collapsed (editor_page); + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + if (!selection_start_marker || !selection_end_marker) + return FALSE; + + if (collapsed) { + WebKitDOMNode *next_block; + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + next_block = webkit_dom_node_get_next_sibling (block); + + /* Next block is quoted content */ + if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_block)) + goto restore; + + /* Delete was pressed in block without any content */ + if (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker))) + goto restore; + + /* If there is just BR element go ahead */ + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)); + if (node && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) + goto restore; + else { + if (key_code != ~0) + save_history_for_delete_or_backspace ( + editor_page, key_code == HTML_KEY_CODE_DELETE, control_key); + + /* Remove the empty block and move caret to the right place. */ + remove_node (block); + + if (delete_key) { + /* To the beginning of the next block. */ + e_editor_dom_move_caret_into_element (editor_page, WEBKIT_DOM_ELEMENT (next_block), TRUE); + } else { + WebKitDOMNode *prev_block; + + /* On the end of previous block. */ + prev_block = webkit_dom_node_get_previous_sibling (next_block); + while (prev_block && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block)) + prev_block = webkit_dom_node_get_last_child (prev_block); + + if (prev_block) + e_editor_dom_move_caret_into_element (editor_page, WEBKIT_DOM_ELEMENT (prev_block), FALSE); + } + + return TRUE; + } + } else { + WebKitDOMNode *end_block, *parent; + + /* Let the quote marks be selectable to nearly correctly remove the + * selection. Corrections after are done in body_keyup_event_cb. */ + enable_quote_marks_select (document); + + parent = webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) || + element_has_tag (WEBKIT_DOM_ELEMENT (parent), "b") || + element_has_tag (WEBKIT_DOM_ELEMENT (parent), "i") || + element_has_tag (WEBKIT_DOM_ELEMENT (parent), "u")) + node = webkit_dom_node_get_previous_sibling (parent); + else + node = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)); + + if (!node || !WEBKIT_DOM_IS_ELEMENT (node)) + goto restore; + + if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-quoted")) + goto restore; + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + end_block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_end_marker)); + + /* Situation where the start of the selection is in the beginning + + * of the block in quoted content and the end in the beginning of + + * content that is after the citation or the selection end is in + + * the end of the quoted content (showed by ^). We have to + + * mark the start block to correctly restore the structure + + * afterwards. + * + * > |xxx + * > xxx^ + * |xxx + */ + if (e_editor_dom_get_citation_level (end_block, FALSE) > 0) { + WebKitDOMNode *parent; + + if (webkit_dom_node_get_next_sibling (end_block)) + goto restore; + + parent = webkit_dom_node_get_parent_node (end_block); + while (parent && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent)) { + WebKitDOMNode *next_parent = webkit_dom_node_get_parent_node (parent); + + if (webkit_dom_node_get_next_sibling (parent) && + !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (next_parent)) + goto restore; + + parent = next_parent; + } + } + } + + restore: + if (key_code != ~0) + save_history_for_delete_or_backspace ( + editor_page, key_code == HTML_KEY_CODE_DELETE, control_key); + + e_editor_dom_selection_restore (editor_page); + + return FALSE; +} + +static gboolean +split_citation (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + WebKitDOMElement *selection_end; + WebKitDOMNode *sibling; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_CITATION_SPLIT; + + e_editor_dom_selection_save (editor_page); + + e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y); + + if (!e_editor_dom_selection_is_collapsed (editor_page)) { + WebKitDOMRange *range = NULL; + + range = e_editor_dom_get_current_range (editor_page); + insert_delete_event (editor_page, range); + + g_clear_object (&range); + + ev->before.end.x = ev->before.start.x; + ev->before.end.y = ev->before.start.y; + } + + selection_end = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end)); + if (!sibling || (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling) && + !element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-wrap-br"))) { + WebKitDOMDocumentFragment *fragment; + + fragment = webkit_dom_document_create_document_fragment (document); + ev->data.fragment = fragment; + } else + ev->data.fragment = NULL; + + e_editor_dom_selection_restore (editor_page); + } + + element = e_editor_dom_insert_new_line_into_citation (editor_page, ""); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + return element != NULL; +} + +static gboolean +delete_last_character_from_previous_line_in_quoted_block (EEditorPage *editor_page, + glong key_code, + guint state) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment = NULL; + WebKitDOMElement *element; + WebKitDOMNode *node, *beginning, *prev_sibling; + EEditorHistoryEvent *ev = NULL; + gboolean hidden_space = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + /* We have to be in quoted content. */ + if (!e_editor_dom_selection_is_citation (editor_page)) + return FALSE; + + /* Selection is just caret. */ + if (!e_editor_dom_selection_is_collapsed (editor_page)) + return FALSE; + + document = e_editor_page_get_document (editor_page); + + e_editor_dom_selection_save (editor_page); + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + /* Before the caret are just quote characters */ + beginning = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + if (!(beginning && WEBKIT_DOM_IS_ELEMENT (beginning))) { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) + beginning = webkit_dom_node_get_previous_sibling (parent); + else + goto out; + } + + /* Before the text is the beginning of line. */ + if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning), "-x-evo-quoted"))) + goto out; + + /* If we are just on the beginning of the line and not on the beginning of + * the block we need to remove the last character ourselves as well, otherwise + * WebKit will put the caret to wrong position. */ + if (!(prev_sibling = webkit_dom_node_get_previous_sibling (beginning))) + goto out; + + if (key_code != ~0) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_DELETE; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + fragment = webkit_dom_document_create_document_fragment (document); + } + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling)) { + if (key_code != ~0) + webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment), prev_sibling, NULL); + else + remove_node (prev_sibling); + } + + prev_sibling = webkit_dom_node_get_previous_sibling (beginning); + if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && + webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (prev_sibling), "data-hidden-space")) { + hidden_space = TRUE; + if (key_code != ~0) + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (fragment), + prev_sibling, + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), + NULL); + else + remove_node (prev_sibling); + } + + node = webkit_dom_node_get_previous_sibling (beginning); + + if (key_code != ~0) + webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment), beginning, NULL); + else + remove_node (beginning); + + if (!hidden_space) { + if (key_code != ~0) { + gchar *data; + + data = webkit_dom_character_data_substring_data ( + WEBKIT_DOM_CHARACTER_DATA (node), + webkit_dom_character_data_get_length ( + WEBKIT_DOM_CHARACTER_DATA (node)) -1, + 1, + NULL); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + webkit_dom_document_create_text_node (document, data)), + NULL); + + g_free (data); + } + + webkit_dom_character_data_delete_data ( + WEBKIT_DOM_CHARACTER_DATA (node), + webkit_dom_character_data_get_length ( + WEBKIT_DOM_CHARACTER_DATA (node)) -1, + 1, + NULL); + } + + if (key_code != ~0) { + EEditorUndoRedoManager *manager; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + ev->data.fragment = fragment; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + + return TRUE; + out: + e_editor_dom_selection_restore (editor_page); + + return FALSE; +} + +gboolean +e_editor_dom_delete_last_character_on_line_in_quoted_block (EEditorPage *editor_page, + glong key_code, + gboolean control_key) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + WebKitDOMNode *node, *beginning, *next_sibling; + gboolean success = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + + /* We have to be in quoted content. */ + if (!e_editor_dom_selection_is_citation (editor_page)) + return FALSE; + + /* Selection is just caret. */ + if (!e_editor_dom_selection_is_collapsed (editor_page)) + return FALSE; + + e_editor_dom_selection_save (editor_page); + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + /* selection end marker */ + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + + /* We have to be on the end of line. */ + next_sibling = webkit_dom_node_get_next_sibling (node); + if (next_sibling && + (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling) || + webkit_dom_node_get_next_sibling (next_sibling))) + goto out; + + /* Before the caret is just text. */ + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + if (!(node && WEBKIT_DOM_IS_TEXT (node))) + goto out; + + /* There is just one character. */ + if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)) != 1) + goto out; + + beginning = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (node)); + if (!(beginning && WEBKIT_DOM_IS_ELEMENT (beginning))) + goto out; + + /* Before the text is the beginning of line. */ + if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning), "-x-evo-quoted"))) + goto out; + + if (!webkit_dom_node_get_previous_sibling (beginning)) + goto out; + + if (key_code != ~0) + save_history_for_delete_or_backspace ( + editor_page, key_code == HTML_KEY_CODE_DELETE, control_key); + + element = webkit_dom_node_get_parent_element (beginning); + remove_node (WEBKIT_DOM_NODE (element)); + + success = TRUE; + out: + e_editor_dom_selection_restore (editor_page); + + if (success) + e_editor_dom_insert_new_line_into_citation (editor_page, NULL); + + return success; +} + +static gboolean +selection_is_in_empty_list_item (WebKitDOMNode *selection_start_marker) +{ + gchar *text; + WebKitDOMNode *sibling; + + /* Selection needs to be collapsed. */ + sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker)); + if (!e_editor_dom_is_selection_position_node (sibling)) + return FALSE; + + /* After the selection end there could be just the BR element. */ + sibling = webkit_dom_node_get_next_sibling (sibling); + if (sibling && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling)) + return FALSE; + + if (sibling && webkit_dom_node_get_next_sibling (sibling)) + return FALSE; + + sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)); + + if (!sibling) + return TRUE; + + /* Only text node with the zero width space character is allowed. */ + if (!WEBKIT_DOM_IS_TEXT (sibling)) + return FALSE; + + if (webkit_dom_node_get_previous_sibling (sibling)) + return FALSE; + + if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (sibling)) != 1) + return FALSE; + + text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (sibling)); + if (!(text && g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0)) { + g_free (text); + return FALSE; + } + + g_free (text); + + return TRUE; +} + +static gboolean +return_pressed_in_image_wrapper (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + WebKitDOMElement *selection_start_marker; + WebKitDOMNode *parent, *block, *clone; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + + if (!e_editor_dom_selection_is_collapsed (editor_page)) + return FALSE; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); + if (!element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-resizable-wrapper")) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INPUT; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + fragment = webkit_dom_document_create_document_fragment (document); + + g_object_set_data ( + G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1)); + } + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + clone = webkit_dom_node_clone_node_with_error (block, FALSE, NULL); + webkit_dom_node_append_child ( + clone, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + clone, + block, + NULL); + + if (ev) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (clone, TRUE, NULL), + NULL); + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + ev->data.fragment = fragment; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_page_emit_content_changed (editor_page); + + e_editor_dom_selection_restore (editor_page); + + return TRUE; +} + +static gboolean +return_pressed_after_h_rule (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + WebKitDOMElement *selection_marker; + WebKitDOMNode *node, *block, *clone, *hr, *insert_before = NULL; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + + if (!e_editor_dom_selection_is_collapsed (editor_page)) + return FALSE; + + e_editor_dom_selection_save (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + selection_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + hr = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_marker)); + hr = webkit_dom_node_get_next_sibling (hr); + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_marker)); + if (node && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node) && + !WEBKIT_DOM_IS_HTML_HR_ELEMENT (hr)) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + insert_before = webkit_dom_node_get_next_sibling (hr); + } else { + selection_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + node = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_marker)); + hr = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_marker)); + if (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node) || + !WEBKIT_DOM_IS_HTML_HR_ELEMENT (hr)) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + insert_before = WEBKIT_DOM_NODE (selection_marker); + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INPUT; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + fragment = webkit_dom_document_create_document_fragment (document); + + g_object_set_data ( + G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1)); + } + + block = webkit_dom_node_get_previous_sibling (hr); + + clone = webkit_dom_node_clone_node_with_error (block, FALSE, NULL); + + webkit_dom_node_append_child ( + clone, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (hr), clone, insert_before, NULL); + + dom_remove_selection_markers (document); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (clone), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, TRUE)), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (clone), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, FALSE)), + NULL); + + if (ev) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (clone, TRUE, NULL), + NULL); + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + ev->data.fragment = fragment; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_page_emit_content_changed (editor_page); + + e_editor_dom_selection_restore (editor_page); + + return TRUE; +} + +gboolean +e_editor_dom_return_pressed_in_empty_list_item (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker; + WebKitDOMNode *parent; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + + if (!e_editor_dom_selection_is_collapsed (editor_page)) + return FALSE; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); + if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent)) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start_marker))) { + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + WebKitDOMDocumentFragment *fragment; + WebKitDOMElement *paragraph; + WebKitDOMNode *list; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INPUT; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + fragment = webkit_dom_document_create_document_fragment (document); + + g_object_set_data ( + G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1)); + } + + list = split_list_into_two (parent, -1); + + if (ev) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + parent, + NULL); + } else { + remove_node (parent); + } + + paragraph = e_editor_dom_prepare_paragraph (editor_page, TRUE); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (list), + WEBKIT_DOM_NODE (paragraph), + list, + NULL); + + remove_node_if_empty (list); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + ev->data.fragment = fragment; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + + e_editor_page_emit_content_changed (editor_page); + + return TRUE; + } + + e_editor_dom_selection_restore (editor_page); + + return FALSE; +} + +static void +process_smiley_on_delete_or_backspace (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + WebKitDOMNode *parent; + gboolean in_smiley = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + e_editor_dom_selection_save (editor_page); + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + if (WEBKIT_DOM_IS_ELEMENT (parent) && + element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text")) + in_smiley = TRUE; + else { + if (e_editor_dom_selection_is_collapsed (editor_page)) { + WebKitDOMNode *prev_sibling; + + prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) { + gchar *text = webkit_dom_character_data_get_data ( + WEBKIT_DOM_CHARACTER_DATA (prev_sibling)); + + if (g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0) { + WebKitDOMNode *prev_prev_sibling; + + prev_prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); + if (WEBKIT_DOM_IS_ELEMENT (prev_prev_sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (prev_prev_sibling), "-x-evo-smiley-wrapper")) { + remove_node (prev_sibling); + in_smiley = TRUE; + parent = webkit_dom_node_get_last_child (prev_prev_sibling); + } + } + + g_free (text); + } + } else { + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + if (WEBKIT_DOM_IS_ELEMENT (parent) && + element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text")) + in_smiley = TRUE; + } + } + + if (in_smiley) { + WebKitDOMNode *wrapper; + + wrapper = webkit_dom_node_get_parent_node (parent); + if (!e_editor_page_get_html_mode (editor_page)) { + WebKitDOMNode *child; + + while ((child = webkit_dom_node_get_first_child (parent))) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (wrapper), + child, + wrapper, + NULL); + } + /* In the HTML mode the whole smiley will be removed. */ + remove_node (wrapper); + /* FIXME history will be probably broken here */ + } + + e_editor_dom_selection_restore (editor_page); +} + +gboolean +e_editor_dom_key_press_event_process_return_key (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNode *table = NULL; + gboolean first_cell = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + /* Return pressed in the beginning of the first cell will insert + * new block before the table (and move the caret there) if none + * is already there, otherwise it will act as normal return. */ + if (selection_is_in_table (document, &first_cell, &table) && first_cell) { + WebKitDOMNode *node; + + node = webkit_dom_node_get_previous_sibling (table); + if (!node) { + node = webkit_dom_node_get_next_sibling (table); + node = webkit_dom_node_clone_node_with_error (node, FALSE, NULL); + webkit_dom_node_append_child ( + node, + WEBKIT_DOM_NODE (webkit_dom_document_create_element ( + document, "br", NULL)), + NULL); + dom_add_selection_markers_into_element_start ( + document, WEBKIT_DOM_ELEMENT (node), NULL, NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (table), + node, + table, + NULL); + e_editor_dom_selection_restore (editor_page); + e_editor_page_emit_content_changed (editor_page); + return TRUE; + } + } + + /* When user presses ENTER in a citation block, WebKit does + * not break the citation automatically, so we need to use + * the special command to do it. */ + if (e_editor_dom_selection_is_citation (editor_page)) { + e_editor_dom_remove_input_event_listener_from_body (editor_page); + if (split_citation (editor_page)) { + e_editor_page_set_return_key_pressed (editor_page, TRUE); + e_editor_dom_check_magic_links (editor_page, FALSE); + e_editor_page_set_return_key_pressed (editor_page, FALSE); + e_editor_page_emit_content_changed (editor_page); + + return TRUE; + } + return FALSE; + } + + /* If the ENTER key is pressed inside an empty list item then the list + * is broken into two and empty paragraph is inserted between lists. */ + if (e_editor_dom_return_pressed_in_empty_list_item (editor_page)) + return TRUE; + + if (return_pressed_in_image_wrapper (editor_page)) + return TRUE; + + if (return_pressed_after_h_rule (editor_page)) + return TRUE; + + return FALSE; +} + +static gboolean +remove_empty_bulleted_list_item (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start; + WebKitDOMNode *parent; + EEditorUndoRedoManager *manager; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + manager = e_editor_page_get_undo_redo_manager (editor_page); + e_editor_dom_selection_save (editor_page); + + selection_start = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start)); + while (parent && !node_is_list_or_item (parent)) + parent = webkit_dom_node_get_parent_node (parent); + + if (!parent) + goto out; + + if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start))) { + EEditorHistoryEvent *ev = NULL; + WebKitDOMDocumentFragment *fragment; + WebKitDOMNode *prev_item; + + prev_item = webkit_dom_node_get_previous_sibling (parent); + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + /* Insert new history event for Return to have the right coordinates. + * The fragment will be added later. */ + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_DELETE; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + fragment = webkit_dom_document_create_document_fragment (document); + } + + if (ev) { + if (prev_item) + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (prev_item, TRUE, NULL), + NULL); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + parent, + NULL); + } else + remove_node (parent); + + if (prev_item) + dom_add_selection_markers_into_element_end ( + document, WEBKIT_DOM_ELEMENT (prev_item), NULL, NULL); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + ev->data.fragment = fragment; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_page_emit_content_changed (editor_page); + e_editor_dom_selection_restore (editor_page); + + return TRUE; + } + out: + e_editor_dom_selection_restore (editor_page); + + return FALSE; +} + +gboolean +e_editor_dom_key_press_event_process_backspace_key (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + /* BackSpace pressed in the beginning of quoted content changes + * format to normal and inserts text into body */ + if (e_editor_dom_selection_is_collapsed (editor_page)) { + e_editor_dom_selection_save (editor_page); + if (e_editor_dom_move_quoted_block_level_up (editor_page) || delete_hidden_space (editor_page)) { + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + e_editor_page_emit_content_changed (editor_page); + return TRUE; + } + e_editor_dom_selection_restore (editor_page); + } + + /* BackSpace in indented block decrease indent level by one */ + if (e_editor_dom_selection_is_indented (editor_page) && + e_editor_dom_selection_is_collapsed (editor_page)) { + WebKitDOMDocument *document; + WebKitDOMElement *selection_start; + WebKitDOMNode *prev_sibling; + + document = e_editor_page_get_document (editor_page); + + e_editor_dom_selection_save (editor_page); + selection_start = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + /* Empty text node before caret */ + prev_sibling = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_start)); + if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) + if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (prev_sibling)) == 0) + prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); + + e_editor_dom_selection_restore (editor_page); + if (!prev_sibling) { + e_editor_dom_selection_unindent (editor_page); + e_editor_page_emit_content_changed (editor_page); + return TRUE; + } + } + + /* BackSpace pressed in an empty item in the bulleted list removes it. */ + if (!e_editor_page_get_html_mode (editor_page) && e_editor_dom_selection_is_collapsed (editor_page) && + remove_empty_bulleted_list_item (editor_page)) + return TRUE; + + + if (prevent_from_deleting_last_element_in_body (e_editor_page_get_document (editor_page))) + return TRUE; + + return FALSE; +} + +gboolean +e_editor_dom_key_press_event_process_delete_or_backspace_key (EEditorPage *editor_page, + glong key_code, + gboolean control_key, + gboolean delete) +{ + WebKitDOMDocument *document; + gboolean html_mode; + gboolean local_delete; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + html_mode = e_editor_page_get_html_mode (editor_page); + local_delete = (key_code == HTML_KEY_CODE_DELETE) || delete; + + if (e_editor_page_get_magic_smileys_enabled (editor_page)) { + /* If deleting something in a smiley it won't be a smiley + * anymore (at least from Evolution' POV), so remove all + * the elements that are hidden in the wrapper and leave + * just the text. Also this ensures that when a smiley is + * recognized and we press the BackSpace key we won't delete + * the UNICODE_HIDDEN_SPACE, but we will correctly delete + * the last character of smiley. */ + process_smiley_on_delete_or_backspace (editor_page); + } + + if (!local_delete && !html_mode && + e_editor_dom_delete_last_character_on_line_in_quoted_block (editor_page, key_code, control_key)) + goto out; + + if (!local_delete && !html_mode && + delete_last_character_from_previous_line_in_quoted_block (editor_page, key_code, control_key)) + goto out; + + if (e_editor_dom_fix_structure_after_delete_before_quoted_content (editor_page, key_code, control_key, FALSE)) + goto out; + + if (local_delete) { + WebKitDOMElement *selection_start_marker; + WebKitDOMNode *sibling, *block, *next_block; + + /* This needs to be performed just in plain text mode + * and when the selection is collapsed. */ + if (html_mode || !e_editor_dom_selection_is_collapsed (editor_page)) + return FALSE; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + sibling = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)); + /* Check if the key was pressed in the beginning of block. */ + if (!(sibling && WEBKIT_DOM_IS_ELEMENT (sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-quoted"))) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + sibling = webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)); + sibling = webkit_dom_node_get_next_sibling (sibling); + + /* And also the current block was empty. */ + if (!(!sibling || (sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling) && + !element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-wrap-br")))) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + next_block = webkit_dom_node_get_next_sibling (block); + + remove_node (block); + + e_editor_dom_move_caret_into_element (editor_page, WEBKIT_DOM_ELEMENT (next_block), TRUE); + + goto out; + } else { + /* Concatenating a non-quoted block with Backspace key to the + * previous block that is inside a quoted content. */ + WebKitDOMElement *selection_start_marker; + WebKitDOMNode *node, *block, *prev_block, *last_child, *child; + + if (html_mode || !e_editor_dom_selection_is_collapsed (editor_page) || + e_editor_dom_selection_is_citation (editor_page)) + return FALSE; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)); + if (node) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + remove_empty_blocks (document); + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + prev_block = webkit_dom_node_get_previous_sibling (block); + if (!prev_block || !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block)) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + last_child = webkit_dom_node_get_last_child (prev_block); + while (last_child && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child)) + last_child = webkit_dom_node_get_last_child (last_child); + + if (!last_child) { + e_editor_dom_selection_restore (editor_page); + return FALSE; + } + + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child)); + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child)); + + node = webkit_dom_node_get_last_child (last_child); + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) + remove_node (node); + + while ((child = webkit_dom_node_get_first_child (block))) + webkit_dom_node_append_child (last_child, child, NULL); + + remove_node (block); + + if (WEBKIT_DOM_IS_ELEMENT (last_child)) + e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (last_child)); + + e_editor_dom_selection_restore (editor_page); + + goto out; + } + + return FALSE; + out: + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + e_editor_page_emit_content_changed (editor_page); + + return TRUE; +} + +static gboolean +contains_forbidden_elements (WebKitDOMDocument *document) +{ + WebKitDOMElement *body, *element; + + body = WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)); + + /* Try to find disallowed elements in the plain text mode */ + element = webkit_dom_element_query_selector ( + body, + ":not(" + /* Basic elements used as blocks allowed in the plain text mode */ + "p[data-evo-paragraph], pre, ul, ol, li, blockquote[type=cite], " + /* Other elements */ + "br, a, " + /* Indented elements */ + ".-x-evo-indented, " + /* Signature */ + ".-x-evo-signature-wrapper, .-x-evo-signature, " + /* Smileys */ + ".-x-evo-smiley-wrapper, .-x-evo-smiley-img, .-x-evo-smiley-text, " + /* Selection markers */ + "#-x-evo-selection-start-marker, #-x-evo-selection-end-marker" + ")", + NULL); + + if (element) + return TRUE; + + /* Try to find disallowed elements relationship in the plain text */ + element = webkit_dom_element_query_selector ( + body, + ":not(" + /* Body descendants */ + "body > :matches(blockquote[type=cite], .-x-evo-signature-wrapper), " + /* Main blocks and indented blocks */ + ":matches(body, .-x-evo-indented) > :matches(pre, p, ul, ol, .-x-evo-indented), " + /* Blockquote descendants */ + "blockquote[type=cite] > :matches(pre, p, blockquote[type=cite]), " + /* Block descendants */ + ":matches(pre, p, li) > :matches(br, span, a), " + /* Lists */ + ":matches(ul, ol) > :matches(ul, ol, li), " + /* Smileys */ + ".-x-evo-smiley-wrapper > :matches(.-x-evo-smiley-img, .-x-evo-smiley-text), " + /* Signature */ + ".-x-evo-signature-wrapper > .-x-evo-signature" + ")", + NULL); + + return element ? TRUE : FALSE; +} + +gboolean +e_editor_dom_check_if_conversion_needed (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + gboolean html_mode, convert = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + html_mode = e_editor_page_get_html_mode (editor_page); + + if (html_mode) + convert = contains_forbidden_elements (document); + + return convert; +} + +void +e_editor_dom_process_content_after_mode_change (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + EEditorUndoRedoManager *manager; + gboolean html_mode; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + html_mode = e_editor_page_get_html_mode (editor_page); + + if (html_mode) + process_content_to_html_changing_composer_mode (editor_page); + else + process_content_to_plain_text_changing_composer_mode (editor_page); + + e_editor_dom_set_monospace_font_family_on_body ( + WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)), html_mode); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + e_editor_undo_redo_manager_clean_history (manager); +} + +guint +e_editor_dom_get_caret_offset (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMNode *anchor; + WebKitDOMRange *range = NULL; + guint ret_val; + gchar *text; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { + g_clear_object (&dom_selection); + return 0; + } + + webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL); + /* Select the text from the current caret position to the beginning of the line. */ + webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "lineBoundary"); + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection); + text = webkit_dom_range_to_string (range, NULL); + ret_val = strlen (text); + g_free (text); + + webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); + + /* In the plain text mode we need to increase the return value by 2 per + * citation level because of "> ". */ + if (!e_editor_page_get_html_mode (editor_page)) { + WebKitDOMNode *parent = anchor; + + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent)) + ret_val += 2; + + parent = webkit_dom_node_get_parent_node (parent); + } + } + + g_clear_object (&range); + g_clear_object (&dom_selection); + + return ret_val; +} + +guint +e_editor_dom_get_caret_position (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL, *range_clone = NULL; + guint ret_val; + gchar *text; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { + g_clear_object (&dom_selection); + return 0; + } + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + range_clone = webkit_dom_range_clone_range (range, NULL); + + body = webkit_dom_document_get_body (document); + /* Select the text from the beginning of the body to the current caret. */ + webkit_dom_range_set_start_before ( + range_clone, webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), NULL); + + /* This is returning a text without new lines! */ + text = webkit_dom_range_to_string (range_clone, NULL); + ret_val = strlen (text); + g_free (text); + + g_clear_object (&range_clone); + g_clear_object (&range); + g_clear_object (&dom_selection); + + return ret_val; +} + +void +e_editor_dom_save_history_for_drop (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMNodeList *list = NULL; + WebKitDOMRange *range = NULL; + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *event; + gint ii, length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + manager = e_editor_page_get_undo_redo_manager (editor_page); + + /* When the image is DnD inside the view WebKit removes the wrapper that + * is used for resizing the image, so we have to recreate it again. */ + list = webkit_dom_document_query_selector_all (document, ":not(span) > img[data-inline]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMElement *element; + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + element = webkit_dom_document_create_element (document, "span", NULL); + webkit_dom_element_set_class_name (element, "-x-evo-resizable-wrapper"); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + node, + NULL); + + webkit_dom_node_append_child (WEBKIT_DOM_NODE (element), node, NULL); + g_object_unref (node); + } + g_clear_object (&list); + + /* When the image is moved the new selection is created after after it, so + * lets collapse the selection to have the caret right after the image. */ + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + + /* Remove the last inserted history event as this one was inserted in + * body_input_event_cb and is wrong as its type is HISTORY_INPUT. */ + /* FIXME we could probably disable the HTML input event callback while + * doing DnD within the view */ + /* FIXME WK2 - what if e_editor_undo_redo_manager_get_current_history_event() returns NULL? */ + if (((EEditorHistoryEvent *) (e_editor_undo_redo_manager_get_current_history_event (manager)))->type == HISTORY_INPUT) + e_editor_undo_redo_manager_remove_current_history_event (manager); + + event = g_new0 (EEditorHistoryEvent, 1); + event->type = HISTORY_INSERT_HTML; + + /* Get the dropped content. It's easy as it is selected by WebKit. */ + fragment = webkit_dom_range_clone_contents (range, NULL); + event->data.string.from = NULL; + /* Get the HTML content of the dropped content. */ + event->data.string.to = dom_get_node_inner_html (WEBKIT_DOM_NODE (fragment)); + + e_editor_dom_selection_get_coordinates (editor_page, + &event->before.start.x, + &event->before.start.y, + &event->before.end.x, + &event->before.end.y); + + event->before.end.x = event->before.start.x; + event->before.end.y = event->before.start.y; + + if (length > 0) + webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL); + else + webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); + + e_editor_dom_selection_get_coordinates (editor_page, + &event->after.start.x, + &event->after.start.y, + &event->after.end.x, + &event->after.end.y); + + e_editor_undo_redo_manager_insert_history_event (manager, event); + + if (!e_editor_page_get_html_mode (editor_page)) { + list = webkit_dom_document_query_selector_all ( + document, "span[style^=font-family]", NULL); + length = webkit_dom_node_list_get_length (list); + if (length > 0) + e_editor_dom_selection_save (editor_page); + + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *span, *child; + + span = webkit_dom_node_list_item (list, ii); + while ((child = webkit_dom_node_get_first_child (span))) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (span), + child, + span, + NULL); + + remove_node (span); + g_object_unref (span); + } + g_clear_object (&list); + + if (length > 0) + e_editor_dom_selection_restore (editor_page); + } + + e_editor_dom_force_spell_check_in_viewport (editor_page); + + g_clear_object (&range); + g_clear_object (&dom_selection); +} + +void +e_editor_dom_drag_and_drop_end (EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + e_editor_dom_save_history_for_drop (editor_page); +} + +static void +dom_set_link_color_in_document (EEditorPage *editor_page, + const gchar *color, + gboolean visited) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLHeadElement *head; + WebKitDOMElement *style_element; + WebKitDOMHTMLElement *body; + gchar *color_str = NULL; + const gchar *style_id; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + g_return_if_fail (color != NULL); + + style_id = visited ? "-x-evo-a-color-style-visited" : "-x-evo-a-color-style"; + + document = e_editor_page_get_document (editor_page); + head = webkit_dom_document_get_head (document); + body = webkit_dom_document_get_body (document); + + style_element = webkit_dom_document_get_element_by_id (document, style_id); + if (!style_element) { + style_element = webkit_dom_document_create_element (document, "style", NULL); + webkit_dom_element_set_id (style_element, style_id); + webkit_dom_element_set_attribute (style_element, "type", "text/css", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style_element), NULL); + } + + color_str = g_strdup_printf ( + visited ? "a.-x-evo-visited-link { color: %s; }" : "a { color: %s; }", color); + webkit_dom_element_set_inner_html (style_element, color_str, NULL); + g_free (color_str); + + if (visited) + webkit_dom_html_body_element_set_v_link ( + WEBKIT_DOM_HTML_BODY_ELEMENT (body), color); + else + webkit_dom_html_body_element_set_link ( + WEBKIT_DOM_HTML_BODY_ELEMENT (body), color); +} + +void +e_editor_dom_set_link_color (EEditorPage *editor_page, + const gchar *color) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + dom_set_link_color_in_document (editor_page, color, FALSE); +} + +void +e_editor_dom_set_visited_link_color (EEditorPage *editor_page, + const gchar *color) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + dom_set_link_color_in_document (editor_page, color, TRUE); +} + +void +e_editor_dom_fix_file_uri_images (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *list = NULL; + gint ii, length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + list = webkit_dom_document_query_selector_all ( + document, "img[src^=\"file://\"]", NULL); + length = webkit_dom_node_list_get_length (list); + + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + gchar *uri; + + node = webkit_dom_node_list_item (list, ii); + uri = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "src"); + g_free (uri); + } + + g_clear_object (&list); +} + +/* ******************** Selection ******************** */ + +void +e_editor_dom_replace_base64_image_src (EEditorPage *editor_page, + const gchar *selector, + const gchar *base64_content, + const gchar *filename, + const gchar *uri) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_query_selector (document, selector, NULL); + + if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (element)) + webkit_dom_html_image_element_set_src ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), + base64_content); + else + webkit_dom_element_set_attribute ( + element, "background", base64_content, NULL); + + webkit_dom_element_set_attribute (element, "data-uri", uri, NULL); + webkit_dom_element_set_attribute (element, "data-inline", "", NULL); + webkit_dom_element_set_attribute ( + element, "data-name", filename ? filename : "", NULL); +} + +WebKitDOMRange * +e_editor_dom_get_current_range (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + if (!dom_window) + return NULL; + + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + if (!WEBKIT_DOM_IS_DOM_SELECTION (dom_selection)) { + g_clear_object (&dom_window); + return NULL; + } + + if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) + goto exit; + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + exit: + g_clear_object (&dom_selection); + g_clear_object (&dom_window); + + return range; +} + +void +e_editor_dom_move_caret_into_element (EEditorPage *editor_page, + WebKitDOMElement *element, + gboolean to_start) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!element) + return; + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + range = webkit_dom_document_create_range (document); + + webkit_dom_range_select_node_contents ( + range, WEBKIT_DOM_NODE (element), NULL); + webkit_dom_range_collapse (range, to_start, NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + + g_clear_object (&range); + g_clear_object (&dom_selection); + g_clear_object (&dom_window); +} + +void +e_editor_dom_insert_base64_image (EEditorPage *editor_page, + const gchar *base64_content, + const gchar *filename, + const gchar *uri) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element, *selection_start_marker, *resizable_wrapper; + WebKitDOMText *text; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (!e_editor_dom_selection_is_collapsed (editor_page)) { + EEditorHistoryEvent *ev; + WebKitDOMDocumentFragment *fragment; + WebKitDOMRange *range = NULL; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_DELETE; + + range = e_editor_dom_get_current_range (editor_page); + fragment = webkit_dom_range_clone_contents (range, NULL); + g_clear_object (&range); + ev->data.fragment = fragment; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->after.start.x = ev->before.start.x; + ev->after.start.y = ev->before.start.y; + ev->after.end.x = ev->before.start.x; + ev->after.end.y = ev->before.start.y; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_AND; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL); + } + + e_editor_dom_selection_save (editor_page); + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_IMAGE; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + } + + resizable_wrapper = + webkit_dom_document_create_element (document, "span", NULL); + webkit_dom_element_set_attribute ( + resizable_wrapper, "class", "-x-evo-resizable-wrapper", NULL); + + element = webkit_dom_document_create_element (document, "img", NULL); + webkit_dom_html_image_element_set_src ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), + base64_content); + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL); + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL); + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (element), "data-name", + filename ? filename : "", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (resizable_wrapper), + WEBKIT_DOM_NODE (element), + NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)), + WEBKIT_DOM_NODE (resizable_wrapper), + WEBKIT_DOM_NODE (selection_start_marker), + NULL); + + /* We have to again use UNICODE_ZERO_WIDTH_SPACE character to restore + * caret on right position */ + text = webkit_dom_document_create_text_node ( + document, UNICODE_ZERO_WIDTH_SPACE); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)), + WEBKIT_DOM_NODE (text), + WEBKIT_DOM_NODE (selection_start_marker), + NULL); + + if (ev) { + WebKitDOMDocumentFragment *fragment; + WebKitDOMNode *node; + + fragment = webkit_dom_document_create_document_fragment (document); + node = webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (resizable_wrapper), TRUE, NULL), + NULL); + + webkit_dom_html_element_insert_adjacent_html ( + WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "​", NULL); + ev->data.fragment = fragment; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + e_editor_dom_scroll_to_caret (editor_page); +} + +/* ************************ image_load_and_insert_async() ************************ */ + +typedef struct _ImageLoadContext { + EEditorPage *editor_page; + GInputStream *input_stream; + GOutputStream *output_stream; + GFile *file; + GFileInfo *file_info; + goffset total_num_bytes; + gssize bytes_read; + const gchar *content_type; + const gchar *filename; + const gchar *selector; + gchar buffer[4096]; +} ImageLoadContext; + +/* Forward Declaration */ +static void +image_load_stream_read_cb (GInputStream *input_stream, + GAsyncResult *result, + ImageLoadContext *load_context); + +static ImageLoadContext * +image_load_context_new (EEditorPage *editor_page) +{ + ImageLoadContext *load_context; + + load_context = g_slice_new0 (ImageLoadContext); + load_context->editor_page = editor_page; + + return load_context; +} + +static void +image_load_context_free (ImageLoadContext *load_context) +{ + if (load_context->input_stream != NULL) + g_object_unref (load_context->input_stream); + + if (load_context->output_stream != NULL) + g_object_unref (load_context->output_stream); + + if (load_context->file_info != NULL) + g_object_unref (load_context->file_info); + + if (load_context->file != NULL) + g_object_unref (load_context->file); + + g_slice_free (ImageLoadContext, load_context); +} + +static void +image_load_finish (ImageLoadContext *load_context) +{ + EEditorPage *editor_page; + GMemoryOutputStream *output_stream; + const gchar *selector; + gchar *base64_encoded, *mime_type, *output, *uri; + gsize size; + gpointer data; + + output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream); + editor_page = load_context->editor_page; + mime_type = g_content_type_get_mime_type (load_context->content_type); + + data = g_memory_output_stream_get_data (output_stream); + size = g_memory_output_stream_get_data_size (output_stream); + uri = g_file_get_uri (load_context->file); + + base64_encoded = g_base64_encode ((const guchar *) data, size); + output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL); + selector = load_context->selector; + if (selector && *selector) + e_editor_dom_replace_base64_image_src (editor_page, selector, output, load_context->filename, uri); + else + e_editor_dom_insert_base64_image (editor_page, output, load_context->filename, uri); + + g_free (base64_encoded); + g_free (output); + g_free (mime_type); + g_free (uri); + + image_load_context_free (load_context); +} + +static void +image_load_write_cb (GOutputStream *output_stream, + GAsyncResult *result, + ImageLoadContext *load_context) +{ + GInputStream *input_stream; + gssize bytes_written; + GError *error = NULL; + + bytes_written = g_output_stream_write_finish ( + output_stream, result, &error); + + if (error) { + image_load_context_free (load_context); + return; + } + + input_stream = load_context->input_stream; + + if (bytes_written < load_context->bytes_read) { + g_memmove ( + load_context->buffer, + load_context->buffer + bytes_written, + load_context->bytes_read - bytes_written); + load_context->bytes_read -= bytes_written; + + g_output_stream_write_async ( + output_stream, + load_context->buffer, + load_context->bytes_read, + G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback) image_load_write_cb, + load_context); + } else + g_input_stream_read_async ( + input_stream, + load_context->buffer, + sizeof (load_context->buffer), + G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback) image_load_stream_read_cb, + load_context); +} + +static void +image_load_stream_read_cb (GInputStream *input_stream, + GAsyncResult *result, + ImageLoadContext *load_context) +{ + GOutputStream *output_stream; + gssize bytes_read; + GError *error = NULL; + + bytes_read = g_input_stream_read_finish ( + input_stream, result, &error); + + if (error) { + image_load_context_free (load_context); + return; + } + + if (bytes_read == 0) { + image_load_finish (load_context); + return; + } + + output_stream = load_context->output_stream; + load_context->bytes_read = bytes_read; + + g_output_stream_write_async ( + output_stream, + load_context->buffer, + load_context->bytes_read, + G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback) image_load_write_cb, + load_context); +} + +static void +image_load_file_read_cb (GFile *file, + GAsyncResult *result, + ImageLoadContext *load_context) +{ + GFileInputStream *input_stream; + GOutputStream *output_stream; + GError *error = NULL; + + /* Input stream might be NULL, so don't use cast macro. */ + input_stream = g_file_read_finish (file, result, &error); + load_context->input_stream = (GInputStream *) input_stream; + + if (error) { + image_load_context_free (load_context); + return; + } + + /* Load the contents into a GMemoryOutputStream. */ + output_stream = g_memory_output_stream_new ( + NULL, 0, g_realloc, g_free); + + load_context->output_stream = output_stream; + + g_input_stream_read_async ( + load_context->input_stream, + load_context->buffer, + sizeof (load_context->buffer), + G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback) image_load_stream_read_cb, + load_context); +} + +static void +image_load_query_info_cb (GFile *file, + GAsyncResult *result, + ImageLoadContext *load_context) +{ + GFileInfo *file_info; + GError *error = NULL; + + file_info = g_file_query_info_finish (file, result, &error); + if (error) { + image_load_context_free (load_context); + return; + } + + load_context->content_type = g_file_info_get_content_type (file_info); + load_context->total_num_bytes = g_file_info_get_size (file_info); + load_context->filename = g_file_info_get_name (file_info); + + g_file_read_async ( + file, G_PRIORITY_DEFAULT, + NULL, (GAsyncReadyCallback) + image_load_file_read_cb, load_context); +} + +static void +image_load_and_insert_async (EEditorPage *editor_page, + const gchar *selector, + const gchar *uri) +{ + ImageLoadContext *load_context; + GFile *file; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + g_return_if_fail (uri && *uri); + + file = g_file_new_for_uri (uri); + g_return_if_fail (file != NULL); + + load_context = image_load_context_new (editor_page); + load_context->file = file; + if (selector && *selector) + load_context->selector = g_strdup (selector); + + g_file_query_info_async ( + file, "standard::*", + G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT, + NULL, (GAsyncReadyCallback) + image_load_query_info_cb, load_context); +} + +void +e_editor_dom_insert_image (EEditorPage *editor_page, + const gchar *uri) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!e_editor_page_get_html_mode (editor_page)) + return; + + if (strstr (uri, ";base64,")) { + if (g_str_has_prefix (uri, "data:")) + e_editor_dom_insert_base64_image (editor_page, uri, "", ""); + if (strstr (uri, ";data")) { + const gchar *base64_data = strstr (uri, ";") + 1; + gchar *filename; + glong filename_length; + + filename_length = + g_utf8_strlen (uri, -1) - + g_utf8_strlen (base64_data, -1) - 1; + filename = g_strndup (uri, filename_length); + + e_editor_dom_insert_base64_image (editor_page, base64_data, filename, ""); + g_free (filename); + } + } else + image_load_and_insert_async (editor_page, NULL, uri); +} + +void +e_editor_dom_replace_image_src (EEditorPage *editor_page, + const gchar *selector, + const gchar *uri) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (strstr (uri, ";base64,")) { + if (g_str_has_prefix (uri, "data:")) + e_editor_dom_replace_base64_image_src ( + editor_page, selector, uri, "", ""); + if (strstr (uri, ";data")) { + const gchar *base64_data = strstr (uri, ";") + 1; + gchar *filename; + glong filename_length; + + filename_length = + g_utf8_strlen (uri, -1) - + g_utf8_strlen (base64_data, -1) - 1; + filename = g_strndup (uri, filename_length); + + e_editor_dom_replace_base64_image_src ( + editor_page, selector, base64_data, filename, ""); + g_free (filename); + } + } else + image_load_and_insert_async (editor_page, selector, uri); +} + +/* + * e_html_editor_selection_unlink: + * @selection: an #EEditorSelection + * + * Removes any links (<A> elements) from current selection or at current + * cursor position. + */ +void +e_editor_dom_selection_unlink (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + WebKitDOMElement *link; + EEditorUndoRedoManager *manager; + gchar *text; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + link = dom_node_find_parent_element ( + webkit_dom_range_get_start_container (range, NULL), "A"); + + g_clear_object (&dom_selection); + g_clear_object (&dom_window); + + if (!link) { + WebKitDOMNode *node; + + /* get element that was clicked on */ + node = webkit_dom_range_get_common_ancestor_container (range, NULL); + if (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) { + link = dom_node_find_parent_element (node, "A"); + if (link && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link)) { + g_clear_object (&range); + return; + } else + link = WEBKIT_DOM_ELEMENT (node); + } + } + + g_clear_object (&range); + + if (!link) + return; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + EEditorHistoryEvent *ev; + WebKitDOMDocumentFragment *fragment; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_REMOVE_LINK; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + fragment = webkit_dom_document_create_document_fragment (document); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (link), TRUE, NULL), + NULL); + ev->data.fragment = fragment; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + text = webkit_dom_html_element_get_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (link)); + webkit_dom_element_set_outer_html (link, text, NULL); + g_free (text); +} + +/* + * e_html_editor_selection_create_link: + * @document: a @WebKitDOMDocument + * @uri: destination of the new link + * + * Converts current selection into a link pointing to @url. + */ +void +e_editor_dom_create_link (EEditorPage *editor_page, + const gchar *uri) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + g_return_if_fail (uri != NULL && *uri != '\0'); + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_CREATE_LINK, uri); +} + +static gint +get_list_level (WebKitDOMNode *node) +{ + gint level = 0; + + while (node && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) { + if (node_is_list (node)) + level++; + node = webkit_dom_node_get_parent_node (node); + } + + return level; +} + +static void +set_ordered_list_type_to_element (WebKitDOMElement *list, + EContentEditorBlockFormat format) +{ + if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST) + webkit_dom_element_remove_attribute (list, "type"); + else if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA) + webkit_dom_element_set_attribute (list, "type", "A", NULL); + else if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN) + webkit_dom_element_set_attribute (list, "type", "I", NULL); +} + +static const gchar * +get_css_alignment_value_class (EContentEditorAlignment alignment) +{ + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_LEFT) + return ""; /* Left is by default on ltr */ + + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_CENTER) + return "-x-evo-align-center"; + + if (alignment == E_CONTENT_EDITOR_ALIGNMENT_RIGHT) + return "-x-evo-align-right"; + + return ""; +} + +/* + * e_html_editor_selection_get_alignment: + * @selection: #an EEditorSelection + * + * Returns alignment of current paragraph + * + * Returns: #EContentEditorAlignment + */ +static EContentEditorAlignment +dom_get_alignment (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMCSSStyleDeclaration *style = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMElement *element; + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + EContentEditorAlignment alignment; + gchar *value; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_CONTENT_EDITOR_ALIGNMENT_LEFT); + + document = e_editor_page_get_document (editor_page); + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return E_CONTENT_EDITOR_ALIGNMENT_LEFT; + + node = webkit_dom_range_get_start_container (range, NULL); + g_clear_object (&range); + if (!node) + return E_CONTENT_EDITOR_ALIGNMENT_LEFT; + + if (WEBKIT_DOM_IS_ELEMENT (node)) + element = WEBKIT_DOM_ELEMENT (node); + else + element = WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child (node)); + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (element)) { + if (element_has_class (element, "-x-evo-align-right")) + alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT; + else if (element_has_class (element, "-x-evo-align-center")) + alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER; + else + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + + return alignment; + } + + dom_window = webkit_dom_document_get_default_view (document); + style = webkit_dom_dom_window_get_computed_style (dom_window, element, NULL); + value = webkit_dom_css_style_declaration_get_property_value (style, "text-align"); + + if (!value || !*value || + (g_ascii_strncasecmp (value, "left", 4) == 0)) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + } else if (g_ascii_strncasecmp (value, "center", 6) == 0) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER; + } else if (g_ascii_strncasecmp (value, "right", 5) == 0) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT; + } else { + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + } + + g_clear_object (&dom_window); + g_clear_object (&style); + g_free (value); + + return alignment; +} + +static gint +set_word_wrap_length (EEditorPage *editor_page, + gint user_word_wrap_length) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + /* user_word_wrap_length < 0, set block width to word_wrap_length + * user_word_wrap_length == 0, no width limit set, + * user_word_wrap_length > 0, set width limit to given value */ + return (user_word_wrap_length < 0) ? + e_editor_page_get_word_wrap_length (editor_page) : user_word_wrap_length; +} + +void +e_editor_dom_set_paragraph_style (EEditorPage *editor_page, + WebKitDOMElement *element, + gint width, + gint offset, + const gchar *style_to_add) +{ + WebKitDOMNode *parent; + gchar *style = NULL; + gint word_wrap_length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + word_wrap_length = set_word_wrap_length (editor_page, width); + webkit_dom_element_set_attribute (element, "data-evo-paragraph", "", NULL); + + /* Don't set the alignment for nodes as they are handled separately. */ + if (!node_is_list (WEBKIT_DOM_NODE (element))) { + EContentEditorAlignment alignment; + + alignment = dom_get_alignment (editor_page); + element_add_class (element, get_css_alignment_value_class (alignment)); + } + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + /* Don't set the width limit to sub-blocks as the width limit is inhered + * from its parents. */ + if (!e_editor_page_get_html_mode (editor_page) && + (!parent || WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent))) { + style = g_strdup_printf ( + "width: %dch;%s%s", + (word_wrap_length + offset), + style_to_add && *style_to_add ? " " : "", + style_to_add && *style_to_add ? style_to_add : ""); + } else { + if (style_to_add && *style_to_add) + style = g_strdup_printf ("%s", style_to_add); + } + if (style) { + webkit_dom_element_set_attribute (element, "style", style, NULL); + g_free (style); + } +} + +static WebKitDOMElement * +create_list_element (EEditorPage *editor_page, + EContentEditorBlockFormat format, + gint level, + gboolean html_mode) +{ + WebKitDOMDocument *document; + WebKitDOMElement *list; + gboolean inserting_unordered_list; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + inserting_unordered_list = format == E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST; + + list = webkit_dom_document_create_element ( + document, inserting_unordered_list ? "UL" : "OL", NULL); + + if (!inserting_unordered_list) + set_ordered_list_type_to_element (list, format); + + if (level >= 0 && !html_mode) { + gint offset; + + offset = (level + 1) * SPACES_PER_LIST_LEVEL; + + offset += !inserting_unordered_list ? + SPACES_ORDERED_LIST_FIRST_LEVEL - SPACES_PER_LIST_LEVEL: 0; + + e_editor_dom_set_paragraph_style (editor_page, list, -1, -offset, NULL); + + if (inserting_unordered_list) + webkit_dom_element_set_attribute (list, "data-evo-plain-text", "", NULL); + } + + return list; +} + +static gboolean +indent_list (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *item, *next_item; + gboolean after_selection_end = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + item = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) { + gboolean html_mode = e_editor_page_get_html_mode (editor_page); + WebKitDOMElement *list; + WebKitDOMNode *source_list = webkit_dom_node_get_parent_node (item); + EContentEditorBlockFormat format; + + format = dom_get_list_format_from_node (source_list); + + list = create_list_element ( + editor_page, format, get_list_level (item), html_mode); + + element_add_class (list, "-x-evo-indented"); + + webkit_dom_node_insert_before ( + source_list, WEBKIT_DOM_NODE (list), item, NULL); + + while (item && !after_selection_end) { + after_selection_end = webkit_dom_node_contains ( + item, WEBKIT_DOM_NODE (selection_end_marker)); + + next_item = webkit_dom_node_get_next_sibling (item); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (list), item, NULL); + + item = next_item; + } + + merge_lists_if_possible (WEBKIT_DOM_NODE (list)); + } + + return after_selection_end; +} + +static void +dom_set_indented_style (EEditorPage *editor_page, + WebKitDOMElement *element, + gint width) +{ + gchar *style; + gint word_wrap_length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + word_wrap_length = set_word_wrap_length (editor_page, width); + webkit_dom_element_set_class_name (element, "-x-evo-indented"); + + if (e_editor_page_get_html_mode (editor_page) || word_wrap_length == 0) { + style = g_strdup_printf ("margin-left: %dch;", SPACES_PER_INDENTATION); + + if (word_wrap_length != 0) { + gchar *plain_text_style; + + plain_text_style = g_strdup_printf ( + "margin-left: %dch; word-wrap: normal; width: %dch;", + SPACES_PER_INDENTATION, word_wrap_length); + + webkit_dom_element_set_attribute ( + element, "data-plain-text-style", plain_text_style, NULL); + g_free (plain_text_style); + } + } else { + style = g_strdup_printf ( + "margin-left: %dch; word-wrap: normal; width: %dch;", + SPACES_PER_INDENTATION, word_wrap_length); + } + + webkit_dom_element_set_attribute (element, "style", style, NULL); + g_free (style); +} + +static WebKitDOMElement * +dom_get_indented_element (EEditorPage *editor_page, + gint width) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_create_element (document, "DIV", NULL); + dom_set_indented_style (editor_page, element, width); + + return element; +} + +static WebKitDOMNode * +indent_block (EEditorPage *editor_page, + WebKitDOMNode *block, + gint width) +{ + WebKitDOMElement *element; + WebKitDOMNode *sibling, *tmp; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + sibling = webkit_dom_node_get_previous_sibling (block); + if (WEBKIT_DOM_IS_ELEMENT (sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-indented")) { + element = WEBKIT_DOM_ELEMENT (sibling); + } else { + element = dom_get_indented_element (editor_page, width); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + WEBKIT_DOM_NODE (element), + block, + NULL); + } + + /* Remove style and let the paragraph inherit it from parent */ + if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), "data-evo-paragraph")) + webkit_dom_element_remove_attribute ( + WEBKIT_DOM_ELEMENT (block), "style"); + + tmp = webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), + block, + NULL); + + sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + + while (WEBKIT_DOM_IS_ELEMENT (sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-indented")) { + WebKitDOMNode *next_sibling; + WebKitDOMNode *child; + + next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (sibling)); + + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (sibling)))) { + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), + child, + NULL); + } + remove_node (sibling); + sibling = next_sibling; + } + + return tmp; +} + +static WebKitDOMNode * +get_list_item_node_from_child (WebKitDOMNode *child) +{ + WebKitDOMNode *parent = webkit_dom_node_get_parent_node (child); + + while (parent && !WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent)) + parent = webkit_dom_node_get_parent_node (parent); + + return parent; +} + +static WebKitDOMNode * +get_list_node_from_child (WebKitDOMNode *child) +{ + WebKitDOMNode *parent = get_list_item_node_from_child (child); + + return webkit_dom_node_get_parent_node (parent); +} + +static gboolean +do_format_change_list_to_block (EEditorPage *editor_page, + EContentEditorBlockFormat format, + WebKitDOMNode *item, + const gchar *value) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element, *selection_end; + WebKitDOMNode *node, *source_list; + gboolean after_end = FALSE; + gint level; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + selection_end = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + source_list = webkit_dom_node_get_parent_node (item); + while (source_list) { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (source_list); + if (node_is_list (parent)) + source_list = parent; + else + break; + } + + if (webkit_dom_node_contains (source_list, WEBKIT_DOM_NODE (selection_end))) + source_list = split_list_into_two (item, -1); + else { + source_list = webkit_dom_node_get_next_sibling (source_list); + } + + /* Process all nodes that are in selection one by one */ + while (item && WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) { + WebKitDOMNode *next_item; + + next_item = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (item)); + if (!next_item) { + WebKitDOMNode *parent; + WebKitDOMNode *tmp = item; + + while (tmp) { + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp)); + if (!node_is_list (parent)) + break; + + next_item = webkit_dom_node_get_next_sibling (parent); + if (node_is_list (next_item)) { + next_item = webkit_dom_node_get_first_child (next_item); + break; + } else if (next_item && !WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item)) { + next_item = webkit_dom_node_get_next_sibling (next_item); + break; + } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item)) { + break; + } + tmp = parent; + } + } else if (node_is_list (next_item)) { + next_item = webkit_dom_node_get_first_child (next_item); + } else if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item)) { + next_item = webkit_dom_node_get_next_sibling (item); + continue; + } + + if (!after_end) { + after_end = webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end)); + + level = get_indentation_level (WEBKIT_DOM_ELEMENT (item)); + + if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH) { + element = e_editor_dom_get_paragraph_element (editor_page, -1, 0); + } else + element = webkit_dom_document_create_element ( + document, value, NULL); + + while ((node = webkit_dom_node_get_first_child (item))) + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), node, NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (source_list), + WEBKIT_DOM_NODE (element), + source_list, + NULL); + + if (level > 0) { + gint final_width = 0; + + node = WEBKIT_DOM_NODE (element); + + if (webkit_dom_element_has_attribute (element, "data-evo-paragraph")) + final_width = e_editor_page_get_word_wrap_length (editor_page) - + SPACES_PER_INDENTATION * level; + + while (level--) + node = indent_block (editor_page, node, final_width); + } + + e_editor_dom_remove_node_and_parents_if_empty (item); + } else + break; + + item = next_item; + } + + remove_node_if_empty (source_list); + + return after_end; +} + +static void +format_change_list_to_block (EEditorPage *editor_page, + EContentEditorBlockFormat format, + const gchar *value) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start; + WebKitDOMNode *item; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + selection_start = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + + item = get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start)); + + do_format_change_list_to_block (editor_page, format, item, value); +} + +static WebKitDOMNode * +get_parent_indented_block (WebKitDOMNode *node) +{ + WebKitDOMNode *parent, *block = NULL; + + parent = webkit_dom_node_get_parent_node (node); + if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) + block = parent; + + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) + block = parent; + parent = webkit_dom_node_get_parent_node (parent); + } + + return block; +} + +static WebKitDOMElement* +get_element_for_inspection (WebKitDOMRange *range) +{ + WebKitDOMNode *node; + + node = webkit_dom_range_get_end_container (range, NULL); + /* No selection or whole body selected */ + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) + return NULL; + + return WEBKIT_DOM_ELEMENT (get_parent_indented_block (node)); +} + +static EContentEditorAlignment +dom_get_alignment_from_node (WebKitDOMNode *node) +{ + EContentEditorAlignment alignment; + gchar *value; + WebKitDOMCSSStyleDeclaration *style = NULL; + + style = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node)); + value = webkit_dom_css_style_declaration_get_property_value (style, "text-align"); + + if (!value || !*value || + (g_ascii_strncasecmp (value, "left", 4) == 0)) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + } else if (g_ascii_strncasecmp (value, "center", 6) == 0) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER; + } else if (g_ascii_strncasecmp (value, "right", 5) == 0) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT; + } else { + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + } + + g_clear_object (&style); + g_free (value); + + return alignment; +} + +/* + * e_html_editor_selection_indent: + * @selection: an #EEditorSelection + * + * Indents current paragraph by one level. + */ +void +e_editor_dom_selection_indent (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *block; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + gboolean after_selection_start = FALSE, after_selection_end = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + e_editor_dom_selection_save (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + /* If the selection was not saved, move it into the first child of body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMHTMLElement *body; + WebKitDOMNode *child; + + body = webkit_dom_document_get_body (document); + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + dom_add_selection_markers_into_element_start ( + document, + WEBKIT_DOM_ELEMENT (child), + &selection_start_marker, + &selection_end_marker); + } + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INDENT; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->data.style.from = 1; + ev->data.style.to = 1; + } + + block = get_parent_indented_block ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (!block) + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + while (block && !after_selection_end) { + gint ii, length, level, word_wrap_length, final_width = 0; + WebKitDOMNode *next_block; + WebKitDOMNodeList *list = NULL; + + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + + next_block = webkit_dom_node_get_next_sibling (block); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (block), + ".-x-evo-indented > *:not(.-x-evo-indented):not(li)", + NULL); + + after_selection_end = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_end_marker)); + + length = webkit_dom_node_list_get_length (list); + if (length == 0 && node_is_list_or_item (block)) { + after_selection_end = indent_list (editor_page); + goto next; + } + + if (length == 0) { + if (!after_selection_start) { + after_selection_start = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_start_marker)); + if (!after_selection_start) + goto next; + } + + if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), "data-evo-paragraph")) { + level = get_indentation_level (WEBKIT_DOM_ELEMENT (block)); + + final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1); + if (final_width < MINIMAL_PARAGRAPH_WIDTH && + !e_editor_page_get_html_mode (editor_page)) + goto next; + } + + indent_block (editor_page, block, final_width); + + if (after_selection_end) + goto next; + } + + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *block_to_process; + + block_to_process = webkit_dom_node_list_item (list, ii); + + after_selection_end = webkit_dom_node_contains ( + block_to_process, WEBKIT_DOM_NODE (selection_end_marker)); + + if (!after_selection_start) { + after_selection_start = webkit_dom_node_contains ( + block_to_process, + WEBKIT_DOM_NODE (selection_start_marker)); + if (!after_selection_start) { + g_object_unref (block_to_process); + continue; + } + } + + if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block_to_process), "data-evo-paragraph")) { + level = get_indentation_level ( + WEBKIT_DOM_ELEMENT (block_to_process)); + + final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1); + if (final_width < MINIMAL_PARAGRAPH_WIDTH && + !e_editor_page_get_html_mode (editor_page)) { + g_object_unref (block_to_process); + continue; + } + } + + indent_block (editor_page, block_to_process, final_width); + + g_object_unref (block_to_process); + if (after_selection_end) + break; + } + + next: + g_clear_object (&list); + + if (!after_selection_end) + block = next_block; + } + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); +} + +static void +unindent_list (WebKitDOMDocument *document) +{ + gboolean after = FALSE; + WebKitDOMElement *new_list; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *source_list, *source_list_clone, *current_list, *item; + WebKitDOMNode *prev_item; + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + if (!selection_start_marker || !selection_end_marker) + return; + + /* Copy elements from previous block to list */ + item = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + source_list = webkit_dom_node_get_parent_node (item); + new_list = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_clone_node_with_error (source_list, FALSE, NULL)); + current_list = source_list; + source_list_clone = webkit_dom_node_clone_node_with_error (source_list, FALSE, NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (source_list), + WEBKIT_DOM_NODE (source_list_clone), + webkit_dom_node_get_next_sibling (source_list), + NULL); + + if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented")) + element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented"); + + prev_item = source_list; + + while (item) { + WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item); + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) { + if (after) + prev_item = webkit_dom_node_append_child ( + source_list_clone, WEBKIT_DOM_NODE (item), NULL); + else + prev_item = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (prev_item), + item, + webkit_dom_node_get_next_sibling (prev_item), + NULL); + } + + if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker))) + after = TRUE; + + if (!next_item) { + if (after) + break; + + current_list = webkit_dom_node_get_next_sibling (current_list); + next_item = webkit_dom_node_get_first_child (current_list); + } + item = next_item; + } + + remove_node_if_empty (source_list_clone); + remove_node_if_empty (source_list); +} + +static void +unindent_block (EEditorPage *editor_page, + WebKitDOMNode *block) +{ + WebKitDOMElement *element; + WebKitDOMElement *prev_blockquote = NULL, *next_blockquote = NULL; + WebKitDOMNode *block_to_process, *node_clone = NULL, *child; + EContentEditorAlignment alignment; + gboolean before_node = TRUE; + gint word_wrap_length, level, width; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + block_to_process = block; + + alignment = dom_get_alignment_from_node (block_to_process); + element = webkit_dom_node_get_parent_element (block_to_process); + + if (!WEBKIT_DOM_IS_HTML_DIV_ELEMENT (element) && + !element_has_class (element, "-x-evo-indented")) + return; + + element_add_class (WEBKIT_DOM_ELEMENT (block_to_process), "-x-evo-to-unindent"); + + level = get_indentation_level (element); + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + width = word_wrap_length - SPACES_PER_INDENTATION * level; + + /* Look if we have previous siblings, if so, we have to + * create new blockquote that will include them */ + if (webkit_dom_node_get_previous_sibling (block_to_process)) + prev_blockquote = dom_get_indented_element (editor_page, width); + + /* Look if we have next siblings, if so, we have to + * create new blockquote that will include them */ + if (webkit_dom_node_get_next_sibling (block_to_process)) + next_blockquote = dom_get_indented_element (editor_page, width); + + /* Copy nodes that are before / after the element that we want to unindent */ + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) { + if (webkit_dom_node_is_equal_node (child, block_to_process)) { + before_node = FALSE; + node_clone = webkit_dom_node_clone_node_with_error (child, TRUE, NULL); + remove_node (child); + continue; + } + + webkit_dom_node_append_child ( + before_node ? + WEBKIT_DOM_NODE (prev_blockquote) : + WEBKIT_DOM_NODE (next_blockquote), + child, + NULL); + } + + if (node_clone) { + element_remove_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-to-unindent"); + + /* Insert blockqoute with nodes that were before the element that we want to unindent */ + if (prev_blockquote) { + if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + WEBKIT_DOM_NODE (prev_blockquote), + WEBKIT_DOM_NODE (element), + NULL); + } + } + + if (level == 1 && webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node_clone), "data-evo-paragraph")) { + e_editor_dom_set_paragraph_style ( + editor_page, WEBKIT_DOM_ELEMENT (node_clone), word_wrap_length, 0, NULL); + element_add_class ( + WEBKIT_DOM_ELEMENT (node_clone), + get_css_alignment_value_class (alignment)); + } + + /* Insert the unindented element */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + node_clone, + WEBKIT_DOM_NODE (element), + NULL); + } else { + g_warn_if_reached (); + } + + /* Insert blockqoute with nodes that were after the element that we want to unindent */ + if (next_blockquote) { + if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (next_blockquote))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + WEBKIT_DOM_NODE (next_blockquote), + WEBKIT_DOM_NODE (element), + NULL); + } + } + + /* Remove old blockquote */ + remove_node (WEBKIT_DOM_NODE (element)); +} + +/* + * dom_unindent: + * @selection: an #EEditorSelection + * + * Unindents current paragraph by one level. + */ +void +e_editor_dom_selection_unindent (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *block; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + gboolean after_selection_start = FALSE, after_selection_end = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + /* If the selection was not saved, move it into the first child of body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMHTMLElement *body; + WebKitDOMNode *child; + + body = webkit_dom_document_get_body (document); + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + dom_add_selection_markers_into_element_start ( + document, + WEBKIT_DOM_ELEMENT (child), + &selection_start_marker, + &selection_end_marker); + } + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_INDENT; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + } + + block = get_parent_indented_block ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (!block) + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + while (block && !after_selection_end) { + gint ii, length; + WebKitDOMNode *next_block; + WebKitDOMNodeList *list = NULL; + + next_block = webkit_dom_node_get_next_sibling (block); + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (block), + ".-x-evo-indented > *:not(.-x-evo-indented):not(li)", + NULL); + + after_selection_end = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_end_marker)); + + length = webkit_dom_node_list_get_length (list); + if (length == 0 && node_is_list_or_item (block)) { + unindent_list (document); + goto next; + } + + if (length == 0) { + if (!after_selection_start) { + after_selection_start = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_start_marker)); + if (!after_selection_start) + goto next; + } + + unindent_block (editor_page, block); + + if (after_selection_end) + goto next; + } + + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *block_to_process; + + block_to_process = webkit_dom_node_list_item (list, ii); + + after_selection_end = webkit_dom_node_contains ( + block_to_process, + WEBKIT_DOM_NODE (selection_end_marker)); + + if (!after_selection_start) { + after_selection_start = webkit_dom_node_contains ( + block_to_process, + WEBKIT_DOM_NODE (selection_start_marker)); + if (!after_selection_start) { + g_object_unref (block_to_process); + continue; + } + } + + unindent_block (editor_page, block_to_process); + + g_object_unref (block_to_process); + if (after_selection_end) + break; + } + next: + g_clear_object (&list); + block = next_block; + } + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); +} + +static WebKitDOMNode * +in_empty_block_in_quoted_content (WebKitDOMNode *element) +{ + WebKitDOMNode *first_child, *next_sibling; + + first_child = webkit_dom_node_get_first_child (element); + if (!WEBKIT_DOM_IS_ELEMENT (first_child)) + return NULL; + + if (!element_has_class (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-quoted")) + return NULL; + + next_sibling = webkit_dom_node_get_next_sibling (first_child); + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling)) + return next_sibling; + + if (!WEBKIT_DOM_IS_ELEMENT (next_sibling)) + return NULL; + + if (!element_has_id (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-selection-start-marker")) + return NULL; + + next_sibling = webkit_dom_node_get_next_sibling (next_sibling); + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling)) + return next_sibling; + + return NULL; +} + +/* + * e_html_editor_selection_save: + * @selection: an #EEditorSelection + * + * Saves current cursor position or current selection range. The selection can + * be later restored by calling e_html_editor_selection_restore(). + * + * Note that calling e_html_editor_selection_save() overwrites previously saved + * position. + * + * Note that this method inserts special markings into the HTML code that are + * used to later restore the selection. It can happen that by deleting some + * segments of the document some of the markings are deleted too. In that case + * restoring the selection by e_html_editor_selection_restore() can fail. Also by + * moving text segments (Cut & Paste) can result in moving the markings + * elsewhere, thus e_html_editor_selection_restore() will restore the selection + * incorrectly. + * + * It is recommended to use this method only when you are not planning to make + * bigger changes to content or structure of the document (formatting changes + * are usually OK). + */ +void +e_editor_dom_selection_save (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + WebKitDOMNode *container, *next_sibling, *marker_node; + WebKitDOMNode *split_node, *parent_node, *anchor; + WebKitDOMElement *start_marker = NULL, *end_marker = NULL; + gboolean collapsed = FALSE; + glong offset, anchor_offset; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + + /* First remove all markers (if present) */ + dom_remove_selection_markers (document); + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { + g_clear_object (&dom_selection); + return; + } + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + if (!range) { + g_clear_object (&dom_selection); + return; + } + + anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection); + anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection); + + collapsed = webkit_dom_range_get_collapsed (range, NULL); + start_marker = dom_create_selection_marker (document, TRUE); + + container = webkit_dom_range_get_start_container (range, NULL); + offset = webkit_dom_range_get_start_offset (range, NULL); + parent_node = webkit_dom_node_get_parent_node (container); + + if (webkit_dom_node_is_same_node (anchor, container) && offset == anchor_offset) + webkit_dom_element_set_attribute (start_marker, "data-anchor", "", NULL); + + if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-quote-character")) { + WebKitDOMNode *node; + + node = webkit_dom_node_get_parent_node ( + webkit_dom_node_get_parent_node (parent_node)); + + if ((next_sibling = in_empty_block_in_quoted_content (node))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (next_sibling), + WEBKIT_DOM_NODE (start_marker), + next_sibling, + NULL); + } else { + webkit_dom_node_insert_before ( + node, + WEBKIT_DOM_NODE (start_marker), + webkit_dom_node_get_next_sibling ( + webkit_dom_node_get_parent_node (parent_node)), + NULL); + } + goto insert_end_marker; + } else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-smiley-text")) { + WebKitDOMNode *node; + + node = webkit_dom_node_get_parent_node (parent_node); + if (offset == 0) { + marker_node = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (start_marker), + webkit_dom_node_get_next_sibling (node), + NULL); + goto insert_end_marker; + } + } else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "Apple-tab-span") && offset == 1) { + marker_node = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent_node), + WEBKIT_DOM_NODE (start_marker), + webkit_dom_node_get_next_sibling (parent_node), + NULL); + goto insert_end_marker; + } + + if (WEBKIT_DOM_IS_TEXT (container)) { + if (offset != 0) { + WebKitDOMText *split_text; + + split_text = webkit_dom_text_split_text ( + WEBKIT_DOM_TEXT (container), offset, NULL); + split_node = WEBKIT_DOM_NODE (split_text); + } else { + marker_node = webkit_dom_node_insert_before ( + parent_node, + WEBKIT_DOM_NODE (start_marker), + container, + NULL); + goto insert_end_marker; + } + } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (container)) { + marker_node = webkit_dom_node_insert_before ( + container, + WEBKIT_DOM_NODE (start_marker), + webkit_dom_node_get_first_child (container), + NULL); + goto insert_end_marker; + } else if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (container)) { + marker_node = webkit_dom_node_insert_before ( + container, + WEBKIT_DOM_NODE (start_marker), + webkit_dom_node_get_first_child (container), + NULL); + goto insert_end_marker; + } else { + /* Insert the selection marker on the right position in + * an empty paragraph in the quoted content */ + if ((next_sibling = in_empty_block_in_quoted_content (container))) { + marker_node = webkit_dom_node_insert_before ( + container, + WEBKIT_DOM_NODE (start_marker), + next_sibling, + NULL); + goto insert_end_marker; + } + if (!webkit_dom_node_get_previous_sibling (container)) { + marker_node = webkit_dom_node_insert_before ( + container, + WEBKIT_DOM_NODE (start_marker), + webkit_dom_node_get_first_child (container), + NULL); + goto insert_end_marker; + } else if (!webkit_dom_node_get_next_sibling (container)) { + WebKitDOMNode *tmp; + + tmp = webkit_dom_node_get_last_child (container); + if (tmp && WEBKIT_DOM_IS_HTML_BR_ELEMENT (tmp)) + marker_node = webkit_dom_node_insert_before ( + container, + WEBKIT_DOM_NODE (start_marker), + tmp, + NULL); + else + marker_node = webkit_dom_node_append_child ( + container, + WEBKIT_DOM_NODE (start_marker), + NULL); + goto insert_end_marker; + } else { + if (webkit_dom_node_get_first_child (container)) { + marker_node = webkit_dom_node_insert_before ( + container, + WEBKIT_DOM_NODE (start_marker), + webkit_dom_node_get_first_child (container), + NULL); + goto insert_end_marker; + } + split_node = container; + } + } + + /* Don't save selection straight into body */ + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (split_node)) { + g_clear_object (&range); + g_clear_object (&dom_selection); + return; + } + + if (!split_node) { + marker_node = webkit_dom_node_insert_before ( + container, + WEBKIT_DOM_NODE (start_marker), + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (container)), + NULL); + } else { + marker_node = WEBKIT_DOM_NODE (start_marker); + parent_node = webkit_dom_node_get_parent_node (split_node); + + webkit_dom_node_insert_before ( + parent_node, marker_node, split_node, NULL); + } + + webkit_dom_node_normalize (parent_node); + + insert_end_marker: + end_marker = dom_create_selection_marker (document, FALSE); + + if (collapsed) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (start_marker)), + WEBKIT_DOM_NODE (end_marker), + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_marker)), + NULL); + goto out; + } + + container = webkit_dom_range_get_end_container (range, NULL); + offset = webkit_dom_range_get_end_offset (range, NULL); + parent_node = webkit_dom_node_get_parent_node (container); + + if (webkit_dom_node_is_same_node (anchor, container) && offset == anchor_offset) + webkit_dom_element_set_attribute (end_marker, "data-anchor", "", NULL); + + if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-quote-character")) { + WebKitDOMNode *node; + + node = webkit_dom_node_get_parent_node ( + webkit_dom_node_get_parent_node (parent_node)); + + if ((next_sibling = in_empty_block_in_quoted_content (node))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (next_sibling), + WEBKIT_DOM_NODE (end_marker), + next_sibling, + NULL); + } else { + webkit_dom_node_insert_before ( + node, + WEBKIT_DOM_NODE (end_marker), + webkit_dom_node_get_next_sibling ( + webkit_dom_node_get_parent_node (parent_node)), + NULL); + } + goto out; + } + + if (WEBKIT_DOM_IS_TEXT (container)) { + if (offset != 0) { + WebKitDOMText *split_text; + + split_text = webkit_dom_text_split_text ( + WEBKIT_DOM_TEXT (container), offset, NULL); + split_node = WEBKIT_DOM_NODE (split_text); + } else { + marker_node = webkit_dom_node_insert_before ( + parent_node, WEBKIT_DOM_NODE (end_marker), container, NULL); + goto check; + + } + } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (container)) { + webkit_dom_node_append_child ( + container, WEBKIT_DOM_NODE (end_marker), NULL); + goto out; + } else { + /* Insert the selection marker on the right position in + * an empty paragraph in the quoted content */ + if ((next_sibling = in_empty_block_in_quoted_content (container))) { + webkit_dom_node_insert_before ( + container, + WEBKIT_DOM_NODE (end_marker), + next_sibling, + NULL); + goto out; + } + if (!webkit_dom_node_get_previous_sibling (container)) { + split_node = parent_node; + } else if (!webkit_dom_node_get_next_sibling (container) && + !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) { + split_node = parent_node; + split_node = webkit_dom_node_get_next_sibling (split_node); + } else + split_node = container; + } + + /* Don't save selection straight into body */ + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (split_node)) { + remove_node (WEBKIT_DOM_NODE (start_marker)); + return; + } + + marker_node = WEBKIT_DOM_NODE (end_marker); + + if (split_node) { + parent_node = webkit_dom_node_get_parent_node (split_node); + + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) { + if (offset == 0) + webkit_dom_node_insert_before ( + split_node, + marker_node, + webkit_dom_node_get_first_child (split_node), + NULL); + else + webkit_dom_node_append_child ( + webkit_dom_node_get_previous_sibling (split_node), + marker_node, + NULL); + } else + webkit_dom_node_insert_before ( + parent_node, marker_node, split_node, NULL); + } else { + WebKitDOMNode *first_child; + + first_child = webkit_dom_node_get_first_child (container); + if (offset == 0 && WEBKIT_DOM_IS_TEXT (first_child)) + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (container), marker_node, webkit_dom_node_get_first_child (container), NULL); + else + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (container), marker_node, NULL); + } + + webkit_dom_node_normalize (parent_node); + + check: + if ((next_sibling = webkit_dom_node_get_next_sibling (marker_node))) { + if (!WEBKIT_DOM_IS_ELEMENT (next_sibling)) + next_sibling = webkit_dom_node_get_next_sibling (next_sibling); + /* If the selection is collapsed ensure that the selection start marker + * is before the end marker */ + if (next_sibling && webkit_dom_node_is_same_node (next_sibling, WEBKIT_DOM_NODE (start_marker))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (marker_node), + next_sibling, + marker_node, + NULL); + } + } + out: + if (!collapsed) { + if (start_marker && end_marker) { + webkit_dom_range_set_start_after (range, WEBKIT_DOM_NODE (start_marker), NULL); + webkit_dom_range_set_end_before (range, WEBKIT_DOM_NODE (end_marker), NULL); + } else { + g_warn_if_reached (); + } + + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + } + + g_clear_object (&range); + g_clear_object (&dom_selection); +} + +gboolean +e_editor_dom_is_selection_position_node (WebKitDOMNode *node) +{ + WebKitDOMElement *element; + + if (!node || !WEBKIT_DOM_IS_ELEMENT (node)) + return FALSE; + + element = WEBKIT_DOM_ELEMENT (node); + + return element_has_id (element, "-x-evo-selection-start-marker") || + element_has_id (element, "-x-evo-selection-end-marker"); +} + +/* + * e_html_editor_selection_restore: + * @selection: an #EEditorSelection + * + * Restores cursor position or selection range that was saved by + * e_html_editor_selection_save(). + * + * Note that calling this function without calling e_html_editor_selection_save() + * before is a programming error and the behavior is undefined. + */ +void +e_editor_dom_selection_restore (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *marker; + WebKitDOMNode *selection_start_marker, *selection_end_marker; + WebKitDOMNode *parent_start, *parent_end, *anchor; + WebKitDOMRange *range = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + gboolean start_is_anchor = FALSE; + glong offset; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + g_clear_object (&dom_window); + if (!range) { + WebKitDOMHTMLElement *body; + + range = webkit_dom_document_create_range (document); + body = webkit_dom_document_get_body (document); + + webkit_dom_range_select_node_contents (range, WEBKIT_DOM_NODE (body), NULL); + webkit_dom_range_collapse (range, TRUE, NULL); + webkit_dom_dom_selection_add_range (dom_selection, range); + } + + selection_start_marker = webkit_dom_range_get_start_container (range, NULL); + if (selection_start_marker) { + gboolean ok = FALSE; + selection_start_marker = + webkit_dom_node_get_next_sibling (selection_start_marker); + + ok = e_editor_dom_is_selection_position_node (selection_start_marker); + + if (ok) { + ok = FALSE; + if (webkit_dom_range_get_collapsed (range, NULL)) { + selection_end_marker = webkit_dom_node_get_next_sibling ( + selection_start_marker); + + ok = e_editor_dom_is_selection_position_node (selection_end_marker); + if (ok) { + WebKitDOMNode *next_sibling; + + next_sibling = webkit_dom_node_get_next_sibling (selection_end_marker); + + if (next_sibling && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling)) { + parent_start = webkit_dom_node_get_parent_node (selection_end_marker); + + remove_node (selection_start_marker); + remove_node (selection_end_marker); + + webkit_dom_node_normalize (parent_start); + g_clear_object (&range); + g_clear_object (&dom_selection); + return; + } + } + } + } + } + + g_clear_object (&range); + range = webkit_dom_document_create_range (document); + if (!range) { + g_clear_object (&dom_selection); + return; + } + + marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (!marker) { + marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + if (marker) + remove_node (WEBKIT_DOM_NODE (marker)); + g_clear_object (&dom_selection); + g_clear_object (&range); + return; + } + + start_is_anchor = webkit_dom_element_has_attribute (marker, "data-anchor"); + parent_start = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker)); + + webkit_dom_range_set_start_after (range, WEBKIT_DOM_NODE (marker), NULL); + remove_node (WEBKIT_DOM_NODE (marker)); + + marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + if (!marker) { + marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (marker) + remove_node (WEBKIT_DOM_NODE (marker)); + g_clear_object (&dom_selection); + g_clear_object (&range); + return; + } + + parent_end = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker)); + + webkit_dom_range_set_end_before (range, WEBKIT_DOM_NODE (marker), NULL); + remove_node (WEBKIT_DOM_NODE (marker)); + + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + if (webkit_dom_node_is_same_node (parent_start, parent_end)) + webkit_dom_node_normalize (parent_start); + else { + webkit_dom_node_normalize (parent_start); + webkit_dom_node_normalize (parent_end); + } + + if (start_is_anchor) { + anchor = webkit_dom_range_get_end_container (range, NULL); + offset = webkit_dom_range_get_end_offset (range, NULL); + + webkit_dom_range_collapse (range, TRUE, NULL); + } else { + anchor = webkit_dom_range_get_start_container (range, NULL); + offset = webkit_dom_range_get_start_offset (range, NULL); + + webkit_dom_range_collapse (range, FALSE, NULL); + } + webkit_dom_dom_selection_add_range (dom_selection, range); + webkit_dom_dom_selection_extend (dom_selection, anchor, offset, NULL); + + g_clear_object (&dom_selection); + g_clear_object (&range); +} + +static gint +find_where_to_break_line (WebKitDOMCharacterData *node, + gint max_length) +{ + gboolean last_break_position_is_dash = FALSE; + gchar *str, *text_start; + gunichar uc; + gint pos = 1, last_break_position = 0, ret_val = 0; + + text_start = webkit_dom_character_data_get_data (node); + + str = text_start; + do { + uc = g_utf8_get_char (str); + if (!uc) { + ret_val = pos <= max_length ? pos : last_break_position > 0 ? last_break_position - 1 : 0; + goto out; + } + + if ((g_unichar_isspace (uc) && !(g_unichar_break_type (uc) == G_UNICODE_BREAK_NON_BREAKING_GLUE)) || + *str == '-') { + if ((last_break_position_is_dash = *str == '-')) { + /* There was no space before the dash */ + if (pos - 1 != last_break_position) { + gchar *rest; + + rest = g_utf8_next_char (str); + if (rest && *rest) { + gunichar next_char; + + /* There is no space after the dash */ + next_char = g_utf8_get_char (rest); + if (g_unichar_isspace (next_char)) + last_break_position_is_dash = FALSE; + else + last_break_position = pos; + } else + last_break_position_is_dash = FALSE; + } else + last_break_position_is_dash = FALSE; + } else + last_break_position = pos; + } + + if ((pos == max_length)) { + /* Look one character after the limit to check if there + * is a space (skip dash) that we are allowed to break at, if so + * break it there. */ + if (*str) { + str = g_utf8_next_char (str); + uc = g_utf8_get_char (str); + + if ((g_unichar_isspace (uc) && + !(g_unichar_break_type (uc) == G_UNICODE_BREAK_NON_BREAKING_GLUE))) + last_break_position = ++pos; + } + break; + } + + pos++; + str = g_utf8_next_char (str); + } while (*str); + + if (last_break_position != 0) + ret_val = last_break_position - 1; + out: + g_free (text_start); + + /* Always break after the dash character. */ + if (last_break_position_is_dash) + ret_val++; + + /* No character to break at is found. We should split at max_length, but + * we will leave the decision on caller as it depends on context. */ + if (ret_val == 0 && last_break_position == 0) + ret_val = -1; + + return ret_val; +} + +/* + * e_html_editor_selection_is_collapsed: + * @selection: an #EEditorSelection + * + * Returns if selection is collapsed. + * + * Returns: Whether the selection is collapsed (just caret) or not (someting is selected). + */ +gboolean +e_editor_dom_selection_is_collapsed (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + gboolean collapsed; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + if (!(dom_window = webkit_dom_document_get_default_view (document))) + return FALSE; + + if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) { + g_clear_object (&dom_window); + return FALSE; + } + + collapsed = webkit_dom_dom_selection_get_is_collapsed (dom_selection); + + g_clear_object (&dom_selection); + + return collapsed; +} + +void +e_editor_dom_scroll_to_caret (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMElement *selection_start_marker; + glong element_top, element_left; + glong window_top, window_left, window_right, window_bottom; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (!selection_start_marker) + return; + + dom_window = webkit_dom_document_get_default_view (document); + + window_top = webkit_dom_dom_window_get_scroll_y (dom_window); + window_left = webkit_dom_dom_window_get_scroll_x (dom_window); + window_bottom = window_top + webkit_dom_dom_window_get_inner_height (dom_window); + window_right = window_left + webkit_dom_dom_window_get_inner_width (dom_window); + + element_left = webkit_dom_element_get_offset_left (selection_start_marker); + element_top = webkit_dom_element_get_offset_top (selection_start_marker); + + /* Check if caret is inside viewport, if not move to it */ + if (!(element_top >= window_top && element_top <= window_bottom && + element_left >= window_left && element_left <= window_right)) { + webkit_dom_element_scroll_into_view (selection_start_marker, TRUE); + } + + e_editor_dom_selection_restore (editor_page); + + g_clear_object (&dom_window); +} + +static void +mark_and_remove_trailing_space (WebKitDOMDocument *document, + WebKitDOMNode *node) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_create_element (document, "SPAN", NULL); + webkit_dom_element_set_attribute (element, "data-hidden-space", "", NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + webkit_dom_node_get_next_sibling (node), + NULL); + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (node), + webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)), + 1, + "", + NULL); +} + +static void +mark_and_remove_leading_space (WebKitDOMDocument *document, + WebKitDOMNode *node) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_create_element (document, "SPAN", NULL); + webkit_dom_element_set_attribute (element, "data-hidden-space", "", NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + node, + NULL); + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL); +} + +static WebKitDOMElement * +wrap_lines (EEditorPage *editor_page, + WebKitDOMNode *block, + gboolean remove_all_br, + gint length_to_wrap, + gint word_wrap_length) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *node, *start_node, *block_clone = NULL; + WebKitDOMNode *start_point = NULL, *first_child, *last_child; + guint line_length; + gulong length_left; + gchar *text_content; + gboolean compensated = FALSE; + gboolean check_next_node = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + + if (!webkit_dom_node_has_child_nodes (block)) + return WEBKIT_DOM_ELEMENT (block); + + /* Avoid wrapping when the block contains just the BR element alone + * or with selection markers. */ + if ((first_child = webkit_dom_node_get_first_child (block)) && + WEBKIT_DOM_IS_HTML_BR_ELEMENT (first_child)) { + WebKitDOMNode *next_sibling; + + if ((next_sibling = webkit_dom_node_get_next_sibling (first_child))) { + if (e_editor_dom_is_selection_position_node (next_sibling) && + (next_sibling = webkit_dom_node_get_next_sibling (next_sibling)) && + e_editor_dom_is_selection_position_node (next_sibling) && + !webkit_dom_node_get_next_sibling (next_sibling)) + return WEBKIT_DOM_ELEMENT (block); + } else + return WEBKIT_DOM_ELEMENT (block); + } + + block_clone = webkit_dom_node_clone_node_with_error (block, TRUE, NULL); + + /* When we wrap, we are wrapping just the text after caret, text + * before the caret is already wrapped, so unwrap the text after + * the caret position */ + selection_end_marker = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block_clone), + "span#-x-evo-selection-end-marker", + NULL); + + if (selection_end_marker) { + WebKitDOMNode *nd = WEBKIT_DOM_NODE (selection_end_marker); + + while (nd) { + WebKitDOMNode *parent_node; + WebKitDOMNode *next_nd = webkit_dom_node_get_next_sibling (nd); + + parent_node = webkit_dom_node_get_parent_node (nd); + if (!next_nd && parent_node && !webkit_dom_node_is_same_node (parent_node, block_clone)) + next_nd = webkit_dom_node_get_next_sibling (parent_node); + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (nd)) { + if (remove_all_br) + remove_node (nd); + else if (element_has_class (WEBKIT_DOM_ELEMENT (nd), "-x-evo-wrap-br")) + remove_node (nd); + } else if (WEBKIT_DOM_IS_ELEMENT (nd) && + webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd), "data-hidden-space")) + webkit_dom_html_element_set_outer_text ( + WEBKIT_DOM_HTML_ELEMENT (nd), " ", NULL); + + nd = next_nd; + } + } else { + gint ii, length; + WebKitDOMNodeList *list = NULL; + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (block_clone), "span[data-hidden-space]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *hidden_space_node; + + hidden_space_node = webkit_dom_node_list_item (list, ii); + webkit_dom_html_element_set_outer_text ( + WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL); + g_object_unref (hidden_space_node); + } + g_clear_object (&list); + } + + /* We have to start from the end of the last wrapped line */ + selection_start_marker = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block_clone), + "span#-x-evo-selection-start-marker", + NULL); + + if (selection_start_marker) { + gboolean first_removed = FALSE; + WebKitDOMNode *nd; + + nd = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)); + while (nd) { + WebKitDOMNode *prev_nd = webkit_dom_node_get_previous_sibling (nd); + + if (!prev_nd && !webkit_dom_node_is_same_node (webkit_dom_node_get_parent_node (nd), block_clone)) + prev_nd = webkit_dom_node_get_previous_sibling (webkit_dom_node_get_parent_node (nd)); + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (nd)) { + if (first_removed) { + start_point = nd; + break; + } else { + remove_node (nd); + first_removed = TRUE; + } + } else if (WEBKIT_DOM_IS_ELEMENT (nd) && + webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd), "data-hidden-space")) { + webkit_dom_html_element_set_outer_text ( + WEBKIT_DOM_HTML_ELEMENT (nd), " ", NULL); + } else if (!prev_nd) { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (nd); + if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) + start_point = nd; + } + + nd = prev_nd; + } + } + + webkit_dom_node_normalize (block_clone); + node = webkit_dom_node_get_first_child (block_clone); + if (node) { + text_content = webkit_dom_node_get_text_content (node); + if (g_strcmp0 ("\n", text_content) == 0) + node = webkit_dom_node_get_next_sibling (node); + g_free (text_content); + } + + if (start_point) { + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (start_point)) + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_point)); + else + node = start_point; + start_node = block_clone; + } else + start_node = node; + + line_length = 0; + while (node) { + gint offset = 0; + WebKitDOMElement *element; + + if (WEBKIT_DOM_IS_TEXT (node)) { + const gchar *newline; + WebKitDOMNode *next_sibling; + + /* If there is temporary hidden space we remove it */ + text_content = webkit_dom_node_get_text_content (node); + if (strstr (text_content, UNICODE_ZERO_WIDTH_SPACE)) { + if (g_str_has_prefix (text_content, UNICODE_ZERO_WIDTH_SPACE)) + webkit_dom_character_data_delete_data ( + WEBKIT_DOM_CHARACTER_DATA (node), + 0, + 1, + NULL); + if (g_str_has_suffix (text_content, UNICODE_ZERO_WIDTH_SPACE)) + webkit_dom_character_data_delete_data ( + WEBKIT_DOM_CHARACTER_DATA (node), + g_utf8_strlen (text_content, -1) - 1, + 1, + NULL); + g_free (text_content); + text_content = webkit_dom_node_get_text_content (node); + } + newline = strstr (text_content, "\n"); + + next_sibling = node; + while (newline) { + next_sibling = WEBKIT_DOM_NODE (webkit_dom_text_split_text ( + WEBKIT_DOM_TEXT (next_sibling), + g_utf8_pointer_to_offset (text_content, newline), + NULL)); + + if (!next_sibling) + break; + + element = webkit_dom_document_create_element ( + document, "BR", NULL); + element_add_class (element, "-x-evo-wrap-br"); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (next_sibling), + WEBKIT_DOM_NODE (element), + next_sibling, + NULL); + + g_free (text_content); + + text_content = webkit_dom_node_get_text_content (next_sibling); + if (g_str_has_prefix (text_content, "\n")) { + webkit_dom_character_data_delete_data ( + WEBKIT_DOM_CHARACTER_DATA (next_sibling), 0, 1, NULL); + g_free (text_content); + text_content = + webkit_dom_node_get_text_content (next_sibling); + } + newline = strstr (text_content, "\n"); + } + g_free (text_content); + } else if (WEBKIT_DOM_IS_ELEMENT (node)) { + if (e_editor_dom_is_selection_position_node (node)) { + if (line_length == 0) { + WebKitDOMNode *tmp_node; + + tmp_node = webkit_dom_node_get_previous_sibling (node); + /* Only check if there is some node before the selection marker. */ + if (tmp_node && !e_editor_dom_is_selection_position_node (tmp_node)) + check_next_node = TRUE; + } + node = webkit_dom_node_get_next_sibling (node); + continue; + } + + check_next_node = FALSE; + /* If element is ANCHOR we wrap it separately */ + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) { + glong anchor_length; + WebKitDOMNode *next_sibling; + + text_content = webkit_dom_node_get_text_content (node); + anchor_length = g_utf8_strlen (text_content, -1); + g_free (text_content); + + next_sibling = webkit_dom_node_get_next_sibling (node); + /* If the anchor doesn't fit on the line move the inner + * nodes out of it and start to wrap them. */ + if ((line_length + anchor_length) > length_to_wrap) { + WebKitDOMNode *inner_node; + + while ((inner_node = webkit_dom_node_get_first_child (node))) { + g_object_set_data ( + G_OBJECT (inner_node), + "-x-evo-anchor-text", + GINT_TO_POINTER (1)); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + inner_node, + next_sibling, + NULL); + } + next_sibling = webkit_dom_node_get_next_sibling (node); + + remove_node (node); + node = next_sibling; + continue; + } + + line_length += anchor_length; + node = next_sibling; + continue; + } + + if (element_has_class (WEBKIT_DOM_ELEMENT (node), "Apple-tab-span")) { + WebKitDOMNode *sibling; + gint tab_length; + + sibling = webkit_dom_node_get_previous_sibling (node); + if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (sibling), "Apple-tab-span")) + tab_length = TAB_LENGTH; + else { + tab_length = TAB_LENGTH - (line_length + compensated ? 0 : (word_wrap_length - length_to_wrap)) % TAB_LENGTH; + compensated = TRUE; + } + + if (line_length + tab_length > length_to_wrap) { + if (webkit_dom_node_get_next_sibling (node)) { + element = webkit_dom_document_create_element ( + document, "BR", NULL); + element_add_class (element, "-x-evo-wrap-br"); + node = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + webkit_dom_node_get_next_sibling (node), + NULL); + } + line_length = 0; + compensated = FALSE; + } else + line_length += tab_length; + + sibling = webkit_dom_node_get_next_sibling (node); + node = sibling; + continue; + } + /* When we are not removing user-entered BR elements (lines wrapped by user), + * we need to skip those elements */ + if (!remove_all_br && WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) { + if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) { + line_length = 0; + compensated = FALSE; + node = webkit_dom_node_get_next_sibling (node); + continue; + } + } + goto next_node; + } else { + WebKitDOMNode *sibling; + + sibling = webkit_dom_node_get_next_sibling (node); + node = sibling; + continue; + } + + /* If length of this node + what we already have is still less + * then length_to_wrap characters, then just concatenate it and + * continue to next node */ + length_left = webkit_dom_character_data_get_length ( + WEBKIT_DOM_CHARACTER_DATA (node)); + + if ((length_left + line_length) <= length_to_wrap) { + if (check_next_node) + goto check_node; + line_length += length_left; + if (line_length == length_to_wrap) { + line_length = 0; + + element = webkit_dom_document_create_element (document, "BR", NULL); + element_add_class (element, "-x-evo-wrap-br"); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + webkit_dom_node_get_next_sibling (node), + NULL); + } + goto next_node; + } + + /* wrap until we have something */ + while (node && (length_left + line_length) > length_to_wrap) { + gboolean insert_and_continue; + gint max_length; + + check_node: + insert_and_continue = FALSE; + + if (!WEBKIT_DOM_IS_CHARACTER_DATA (node)) + goto next_node; + + element = webkit_dom_document_create_element (document, "BR", NULL); + element_add_class (element, "-x-evo-wrap-br"); + + max_length = length_to_wrap - line_length; + if (max_length < 0) + max_length = length_to_wrap; + else if (max_length == 0) { + if (check_next_node) { + insert_and_continue = TRUE; + goto check; + } + + /* Break before the current node and continue. */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + node, + NULL); + line_length = 0; + continue; + } + + /* Allow anchors to break on any character. */ + if (g_object_steal_data (G_OBJECT (node), "-x-evo-anchor-text")) + offset = max_length; + else { + /* Find where we can line-break the node so that it + * effectively fills the rest of current row. */ + offset = find_where_to_break_line ( + WEBKIT_DOM_CHARACTER_DATA (node), max_length); + + /* When pressing delete on the end of line to concatenate + * the last word from the line and first word from the + * next line we will end with the second word split + * somewhere in the middle (to be precise it will be + * split after the last character that will fit on the + * previous line. To avoid that we need to put the + * concatenated word on the next line. */ + if (offset == -1 || check_next_node) { + WebKitDOMNode *prev_sibling; + + check: + check_next_node = FALSE; + prev_sibling = webkit_dom_node_get_previous_sibling (node); + if (prev_sibling && e_editor_dom_is_selection_position_node (prev_sibling)) { + WebKitDOMNode *prev_br = NULL; + + prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); + + /* Collapsed selection */ + if (prev_sibling && e_editor_dom_is_selection_position_node (prev_sibling)) + prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); + + if (prev_sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-wrap-br")) { + prev_br = prev_sibling; + prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); + } + + if (prev_sibling && WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling)) { + gchar *data; + glong text_length, length = 0; + + data = webkit_dom_character_data_get_data ( + WEBKIT_DOM_CHARACTER_DATA (prev_sibling)); + text_length = webkit_dom_character_data_get_length ( + WEBKIT_DOM_CHARACTER_DATA (prev_sibling)); + + /* Find the last character where we can break. */ + while (text_length - length > 0) { + if (strchr (" ", data[text_length - length - 1])) { + length++; + break; + } else if (data[text_length - length - 1] == '-' && + text_length - length > 1 && + !strchr (" ", data[text_length - length - 2])) + break; + length++; + } + + if (text_length != length) { + WebKitDOMNode *nd; + + webkit_dom_text_split_text ( + WEBKIT_DOM_TEXT (prev_sibling), + text_length - length, + NULL); + + if ((nd = webkit_dom_node_get_next_sibling (prev_sibling))) { + gchar *nd_content; + + nd_content = webkit_dom_node_get_text_content (nd); + if (nd_content && *nd_content) { + if (*nd_content == ' ') + mark_and_remove_leading_space (document, nd); + + if (!webkit_dom_node_get_next_sibling (nd) && + g_str_has_suffix (nd_content, " ")) + mark_and_remove_trailing_space (document, nd); + + g_free (nd_content); + } + + if (nd) { + if (prev_br) + remove_node (prev_br); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (nd), + WEBKIT_DOM_NODE (element), + nd, + NULL); + + offset = 0; + line_length = length; + continue; + } + } + } + } + } + if (insert_and_continue) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + node, + NULL); + + offset = 0; + line_length = 0; + insert_and_continue = FALSE; + continue; + } + + offset = offset != -1 ? offset : max_length; + } + } + + if (offset >= 0) { + WebKitDOMNode *nd; + + if (offset != length_left && offset != 0) { + webkit_dom_text_split_text ( + WEBKIT_DOM_TEXT (node), offset, NULL); + + nd = webkit_dom_node_get_next_sibling (node); + } else + nd = node; + + if (nd) { + gboolean no_sibling = FALSE; + gchar *nd_content; + + nd_content = webkit_dom_node_get_text_content (nd); + if (nd_content && *nd_content) { + if (*nd_content == ' ') + mark_and_remove_leading_space (document, nd); + + if (!webkit_dom_node_get_next_sibling (nd) && + length_left <= length_to_wrap && + g_str_has_suffix (nd_content, " ")) { + mark_and_remove_trailing_space (document, nd); + no_sibling = TRUE; + } + + g_free (nd_content); + } + + if (!no_sibling) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + nd, + NULL); + + offset = 0; + + nd_content = webkit_dom_node_get_text_content (nd); + if (!*nd_content) + remove_node (nd); + g_free (nd_content); + + if (no_sibling) + node = NULL; + else + node = webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (element)); + } else { + webkit_dom_node_append_child ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + NULL); + } + } + if (node && WEBKIT_DOM_IS_CHARACTER_DATA (node)) + length_left = webkit_dom_character_data_get_length ( + WEBKIT_DOM_CHARACTER_DATA (node)); + + line_length = 0; + compensated = FALSE; + } + line_length += length_left - offset; + next_node: + if (!node) + break; + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node)) { + line_length = 0; + compensated = FALSE; + } + + /* Move to next node */ + if (webkit_dom_node_has_child_nodes (node)) { + node = webkit_dom_node_get_first_child (node); + } else if (webkit_dom_node_get_next_sibling (node)) { + node = webkit_dom_node_get_next_sibling (node); + } else { + WebKitDOMNode *tmp_parent; + + if (webkit_dom_node_is_equal_node (node, start_node)) + break; + + /* Find a next node that we can process. */ + tmp_parent = webkit_dom_node_get_parent_node (node); + if (tmp_parent && webkit_dom_node_get_next_sibling (tmp_parent)) + node = webkit_dom_node_get_next_sibling (tmp_parent); + else { + WebKitDOMNode *tmp; + + tmp = tmp_parent; + /* Find a node that is not a start node (that would mean + * that we already processed the whole block) and it has + * a sibling that we can process. */ + while (tmp && !webkit_dom_node_is_equal_node (tmp, start_node) && + !webkit_dom_node_get_next_sibling (tmp)) { + tmp = webkit_dom_node_get_parent_node (tmp); + } + + /* If we found a node to process, let's process its + * sibling, otherwise give up. */ + if (tmp) + node = webkit_dom_node_get_next_sibling (tmp); + else + break; + } + } + } + + last_child = webkit_dom_node_get_last_child (block_clone); + if (last_child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (last_child) && + element_has_class (WEBKIT_DOM_ELEMENT (last_child), "-x-evo-wrap-br")) + remove_node (last_child); + + webkit_dom_node_normalize (block_clone); + + node = webkit_dom_node_get_parent_node (block); + if (node) { + /* Replace block with wrapped one */ + webkit_dom_node_replace_child ( + node, block_clone, block, NULL); + } + + return WEBKIT_DOM_ELEMENT (block_clone); +} + +void +e_editor_dom_remove_wrapping_from_element (WebKitDOMElement *element) +{ + WebKitDOMNodeList *list = NULL; + gint ii, length; + + g_return_if_fail (element != NULL); + + list = webkit_dom_element_query_selector_all ( + element, "br.-x-evo-wrap-br", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + WebKitDOMNode *parent; + + parent = e_editor_dom_get_parent_block_node_from_child (node); + if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-user-wrapped")) + remove_node (node); + g_object_unref (node); + } + + g_clear_object (&list); + + list = webkit_dom_element_query_selector_all ( + element, "span[data-hidden-space]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *hidden_space_node; + WebKitDOMNode *parent; + + hidden_space_node = webkit_dom_node_list_item (list, ii); + parent = e_editor_dom_get_parent_block_node_from_child (hidden_space_node); + if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-user-wrapped")) { + webkit_dom_html_element_set_outer_text ( + WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL); + } + g_object_unref (hidden_space_node); + } + g_clear_object (&list); + + webkit_dom_node_normalize (WEBKIT_DOM_NODE (element)); +} + +void +e_editor_dom_remove_quoting_from_element (WebKitDOMElement *element) +{ + gint ii, length; + WebKitDOMNodeList *list = NULL; + + g_return_if_fail (element != NULL); + + list = webkit_dom_element_query_selector_all ( + element, "span.-x-evo-quoted", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); + + list = webkit_dom_element_query_selector_all ( + element, "br.-x-evo-temp-br", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + remove_node (node); + g_object_unref (node); + } + g_clear_object (&list); + + webkit_dom_node_normalize (WEBKIT_DOM_NODE (element)); +} + +WebKitDOMElement * +e_editor_dom_get_paragraph_element (EEditorPage *editor_page, + gint width, + gint offset) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_create_element (document, "p", NULL); + e_editor_dom_set_paragraph_style (editor_page, element, width, offset, NULL); + + return element; +} + +WebKitDOMElement * +e_editor_dom_put_node_into_paragraph (EEditorPage *editor_page, + WebKitDOMNode *node, + gboolean with_input) +{ + WebKitDOMDocument *document; + WebKitDOMRange *range = NULL; + WebKitDOMElement *container; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + range = webkit_dom_document_create_range (document); + container = e_editor_dom_get_paragraph_element (editor_page, -1, 0); + webkit_dom_range_select_node (range, node, NULL); + webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (container), NULL); + /* We have to move caret position inside this container */ + if (with_input) + dom_add_selection_markers_into_element_end (document, container, NULL, NULL); + + g_clear_object (&range); + + return container; +} + +static gint +selection_get_citation_level (WebKitDOMNode *node) +{ + WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); + gint level = 0; + + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && + webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type")) + level++; + + parent = webkit_dom_node_get_parent_node (parent); + } + + return level; +} + +WebKitDOMElement * +e_editor_dom_wrap_paragraph_length (EEditorPage *editor_page, + WebKitDOMElement *paragraph, + gint length) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL); + g_return_val_if_fail (length >= MINIMAL_PARAGRAPH_WIDTH, NULL); + + return wrap_lines (editor_page, WEBKIT_DOM_NODE (paragraph), FALSE, length, + e_editor_page_get_word_wrap_length (editor_page)); +} + +/* + * e_html_editor_selection_wrap_lines: + * @selection: an #EEditorSelection + * + * Wraps all lines in current selection to be 71 characters long. + */ + +void +e_editor_dom_selection_wrap (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *block, *next_block; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + gboolean after_selection_end = FALSE, html_mode; + gint word_wrap_length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + + e_editor_dom_selection_save (editor_page); + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + /* If the selection was not saved, move it into the first child of body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMHTMLElement *body; + WebKitDOMNode *child; + + body = webkit_dom_document_get_body (document); + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + dom_add_selection_markers_into_element_start ( + document, + WEBKIT_DOM_ELEMENT (child), + &selection_start_marker, + &selection_end_marker); + } + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_WRAP; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->data.style.from = 1; + ev->data.style.to = 1; + } + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + html_mode = e_editor_page_get_html_mode (editor_page); + + /* Process all blocks that are in the selection one by one */ + while (block && !after_selection_end) { + gboolean quoted = FALSE; + gint citation_level, quote; + WebKitDOMElement *wrapped_paragraph; + + next_block = webkit_dom_node_get_next_sibling (block); + + /* Don't try to wrap the 'Normal' blocks as they are already wrapped and*/ + /* also skip blocks that we already wrapped with this function. */ + if ((!html_mode && webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), "data-evo-paragraph")) || + webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), "data-user-wrapped")) { + block = next_block; + continue; + } + + if (webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) { + quoted = TRUE; + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block)); + } + + if (!html_mode) + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block)); + + after_selection_end = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_end_marker)); + + citation_level = selection_get_citation_level (block); + quote = citation_level ? citation_level * 2 : 0; + + wrapped_paragraph = e_editor_dom_wrap_paragraph_length ( + editor_page, WEBKIT_DOM_ELEMENT (block), word_wrap_length - quote); + + webkit_dom_element_set_attribute ( + wrapped_paragraph, "data-user-wrapped", "", NULL); + + if (quoted && !html_mode) + e_editor_dom_quote_plain_text_element (editor_page, wrapped_paragraph); + + block = next_block; + } + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + + e_editor_dom_force_spell_check_in_viewport (editor_page); +} + +void +e_editor_dom_wrap_paragraphs_in_document (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *list = NULL; + gint ii, length; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + list = webkit_dom_document_query_selector_all ( + document, "[data-evo-paragraph]:not(#-x-evo-input-start)", NULL); + + length = webkit_dom_node_list_get_length (list); + + for (ii = 0; ii < length; ii++) { + gint word_wrap_length, quote, citation_level; + WebKitDOMNode *node = webkit_dom_node_list_item (list, ii); + + citation_level = selection_get_citation_level (node); + quote = citation_level ? citation_level * 2 : 0; + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + + if (node_is_list (node)) { + WebKitDOMNode *item = webkit_dom_node_get_first_child (node); + + while (item && WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) { + e_editor_dom_wrap_paragraph_length ( + editor_page, WEBKIT_DOM_ELEMENT (item), word_wrap_length - quote); + item = webkit_dom_node_get_next_sibling (item); + } + } else { + e_editor_dom_wrap_paragraph_length ( + editor_page, WEBKIT_DOM_ELEMENT (node), word_wrap_length - quote); + } + g_object_unref (node); + } + g_clear_object (&list); +} + +WebKitDOMElement * +e_editor_dom_wrap_paragraph (EEditorPage *editor_page, + WebKitDOMElement *paragraph) +{ + gint indentation_level, citation_level, quote; + gint word_wrap_length, final_width, offset = 0; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL); + + indentation_level = get_indentation_level (paragraph); + citation_level = selection_get_citation_level (WEBKIT_DOM_NODE (paragraph)); + + if (node_is_list_or_item (WEBKIT_DOM_NODE (paragraph))) { + gint list_level = get_list_level (WEBKIT_DOM_NODE (paragraph)); + indentation_level = 0; + + if (list_level > 0) + offset = list_level * -SPACES_PER_LIST_LEVEL; + else + offset = -SPACES_PER_LIST_LEVEL; + } + + quote = citation_level ? citation_level * 2 : 0; + + word_wrap_length = e_editor_page_get_word_wrap_length (editor_page); + final_width = word_wrap_length - quote + offset; + final_width -= SPACES_PER_INDENTATION * indentation_level; + + return e_editor_dom_wrap_paragraph_length ( + editor_page, WEBKIT_DOM_ELEMENT (paragraph), final_width); +} + +static gboolean +get_has_style (EEditorPage *editor_page, + const gchar *style_tag) +{ + WebKitDOMNode *node; + WebKitDOMElement *element; + WebKitDOMRange *range = NULL; + gboolean result; + gint tag_len; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return FALSE; + + node = webkit_dom_range_get_start_container (range, NULL); + if (WEBKIT_DOM_IS_ELEMENT (node)) + element = WEBKIT_DOM_ELEMENT (node); + else + element = webkit_dom_node_get_parent_element (node); + g_clear_object (&range); + + tag_len = strlen (style_tag); + result = FALSE; + while (!result && element) { + gchar *element_tag; + gboolean accept_citation = FALSE; + + element_tag = webkit_dom_element_get_tag_name (element); + + if (g_ascii_strncasecmp (style_tag, "citation", 8) == 0) { + accept_citation = TRUE; + result = WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element); + if (element_has_class (element, "-x-evo-indented")) + result = FALSE; + } else { + result = ((tag_len == strlen (element_tag)) && + (g_ascii_strncasecmp (element_tag, style_tag, tag_len) == 0)); + } + + /* Special case: <blockquote type=cite> marks quotation, while + * just <blockquote> is used for indentation. If the <blockquote> + * has type=cite, then ignore it unless style_tag is "citation" */ + if (result && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element)) { + if (webkit_dom_element_has_attribute (element, "type")) { + gchar *type = webkit_dom_element_get_attribute (element, "type"); + if (!accept_citation && (type && g_ascii_strncasecmp (type, "cite", 4) == 0)) { + result = FALSE; + } + g_free (type); + } else { + if (accept_citation) + result = FALSE; + } + } + + g_free (element_tag); + + if (result) + break; + + element = webkit_dom_node_get_parent_element ( + WEBKIT_DOM_NODE (element)); + } + + return result; +} + +typedef gboolean (*IsRightFormatNodeFunc) (WebKitDOMElement *element); + +static gboolean +dom_selection_is_font_format (EEditorPage *editor_page, + IsRightFormatNodeFunc func, + gboolean *previous_value) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMNode *start, *end, *sibling; + WebKitDOMRange *range = NULL; + gboolean ret_val = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + if (!e_editor_page_get_html_mode (editor_page)) + goto out; + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (!webkit_dom_dom_selection_get_range_count (dom_selection)) + goto out; + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + if (!range) + goto out; + + if (webkit_dom_range_get_collapsed (range, NULL) && previous_value) { + WebKitDOMNode *node; + gchar* text_content; + + node = webkit_dom_range_get_common_ancestor_container (range, NULL); + /* If we are changing the format of block we have to re-set the + * format property, otherwise it will be turned off because of no + * text in block. */ + text_content = webkit_dom_node_get_text_content (node); + if (g_strcmp0 (text_content, "") == 0) { + g_free (text_content); + ret_val = *previous_value; + goto out; + } + g_free (text_content); + } + + /* Range without start or end point is a wrong range. */ + start = webkit_dom_range_get_start_container (range, NULL); + end = webkit_dom_range_get_end_container (range, NULL); + if (!start || !end) + goto out; + + if (WEBKIT_DOM_IS_TEXT (start)) + start = webkit_dom_node_get_parent_node (start); + while (start && WEBKIT_DOM_IS_ELEMENT (start) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (start)) { + /* Find the start point's parent node with given formatting. */ + if (func (WEBKIT_DOM_ELEMENT (start))) { + ret_val = TRUE; + break; + } + start = webkit_dom_node_get_parent_node (start); + } + + /* Start point doesn't have the given formatting. */ + if (!ret_val) + goto out; + + /* If the selection is collapsed, we can return early. */ + if (webkit_dom_range_get_collapsed (range, NULL)) + goto out; + + /* The selection is in the same node and that node is supposed to have + * the same formatting (otherwise it is split up with formatting element. */ + if (webkit_dom_node_is_same_node ( + webkit_dom_range_get_start_container (range, NULL), + webkit_dom_range_get_end_container (range, NULL))) + goto out; + + ret_val = FALSE; + + if (WEBKIT_DOM_IS_TEXT (end)) + end = webkit_dom_node_get_parent_node (end); + while (end && WEBKIT_DOM_IS_ELEMENT (end) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (end)) { + /* Find the end point's parent node with given formatting. */ + if (func (WEBKIT_DOM_ELEMENT (end))) { + ret_val = TRUE; + break; + } + end = webkit_dom_node_get_parent_node (end); + } + + if (!ret_val) + goto out; + + ret_val = FALSE; + + /* Now go between the end points and check the inner nodes for format validity. */ + sibling = start; + while ((sibling = webkit_dom_node_get_next_sibling (sibling))) { + if (webkit_dom_node_is_same_node (sibling, end)) { + ret_val = TRUE; + goto out; + } + + if (WEBKIT_DOM_IS_TEXT (sibling)) + goto out; + else if (func (WEBKIT_DOM_ELEMENT (sibling))) + continue; + else if (webkit_dom_node_get_first_child (sibling)) { + WebKitDOMNode *first_child; + + first_child = webkit_dom_node_get_first_child (sibling); + if (!webkit_dom_node_get_next_sibling (first_child)) + if (WEBKIT_DOM_IS_ELEMENT (first_child) && func (WEBKIT_DOM_ELEMENT (first_child))) + continue; + else + goto out; + else + goto out; + } else + goto out; + } + + sibling = end; + while ((sibling = webkit_dom_node_get_previous_sibling (sibling))) { + if (webkit_dom_node_is_same_node (sibling, start)) + break; + + if (WEBKIT_DOM_IS_TEXT (sibling)) + goto out; + else if (func (WEBKIT_DOM_ELEMENT (sibling))) + continue; + else if (webkit_dom_node_get_first_child (sibling)) { + WebKitDOMNode *first_child; + + first_child = webkit_dom_node_get_first_child (sibling); + if (!webkit_dom_node_get_next_sibling (first_child)) + if (WEBKIT_DOM_IS_ELEMENT (first_child) && func (WEBKIT_DOM_ELEMENT (first_child))) + continue; + else + goto out; + else + goto out; + } else + goto out; + } + + ret_val = TRUE; + out: + g_clear_object (&range); + g_clear_object (&dom_selection); + + return ret_val; +} + +static gboolean +is_underline_element (WebKitDOMElement *element) +{ + if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + return element_has_tag (element, "u"); +} + +/* + * e_html_editor_selection_is_underline: + * @selection: an #EEditorSelection + * + * Returns whether current selection or letter at current cursor position + * is underlined. + * + * Returns @TRUE when selection is underlined, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_underline (EEditorPage *editor_page) +{ + gboolean is_underline; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + is_underline = e_editor_page_get_underline (editor_page); + is_underline = dom_selection_is_font_format ( + editor_page, (IsRightFormatNodeFunc) is_underline_element, &is_underline); + + return is_underline; +} + +static WebKitDOMElement * +set_font_style (WebKitDOMDocument *document, + const gchar *element_name, + gboolean value) +{ + WebKitDOMElement *element; + WebKitDOMNode *parent; + + element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker"); + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + if (value) { + WebKitDOMNode *node; + WebKitDOMElement *el; + gchar *name; + + el = webkit_dom_document_create_element (document, element_name, NULL); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (el), UNICODE_ZERO_WIDTH_SPACE, NULL); + + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (el), node, NULL); + name = webkit_dom_node_get_local_name (parent); + if (g_strcmp0 (name, element_name) == 0 && g_strcmp0 (name, "font") != 0) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + WEBKIT_DOM_NODE (el), + webkit_dom_node_get_next_sibling (parent), + NULL); + else + webkit_dom_node_insert_before ( + parent, + WEBKIT_DOM_NODE (el), + WEBKIT_DOM_NODE (element), + NULL); + g_free (name); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (el), WEBKIT_DOM_NODE (element), NULL); + + return el; + } else { + gboolean no_sibling; + WebKitDOMNode *node, *sibling; + + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + + /* Turning the formatting in the middle of element. */ + sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + no_sibling = sibling && + !WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling) && + !webkit_dom_node_get_next_sibling (sibling); + + if (no_sibling) { + WebKitDOMNode *clone; + WebKitDOMNode *sibling; + + clone = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (parent), FALSE, NULL); + + while ((sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)))) + webkit_dom_node_insert_before ( + clone, + sibling, + webkit_dom_node_get_first_child (clone), + NULL); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + clone, + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)), + NULL); + } + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + WEBKIT_DOM_NODE (element), + webkit_dom_node_get_next_sibling (parent), + NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + node, + webkit_dom_node_get_next_sibling (parent), + NULL); + + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling) && !no_sibling) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + node, + webkit_dom_node_get_next_sibling (parent), + NULL); + } + + webkit_dom_html_element_insert_adjacent_text ( + WEBKIT_DOM_HTML_ELEMENT (parent), + "afterend", + UNICODE_ZERO_WIDTH_SPACE, + NULL); + + remove_node_if_empty (parent); + } + + return NULL; +} + +static void +selection_set_font_style (EEditorPage *editor_page, + EContentEditorCommand command, + gboolean value) +{ + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + e_editor_dom_selection_save (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + if (command == E_CONTENT_EDITOR_COMMAND_BOLD) + ev->type = HISTORY_BOLD; + else if (command == E_CONTENT_EDITOR_COMMAND_ITALIC) + ev->type = HISTORY_ITALIC; + else if (command == E_CONTENT_EDITOR_COMMAND_UNDERLINE) + ev->type = HISTORY_UNDERLINE; + else if (command == E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH) + ev->type = HISTORY_STRIKETHROUGH; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->data.style.from = !value; + ev->data.style.to = value; + } + + if (e_editor_dom_selection_is_collapsed (editor_page)) { + const gchar *element_name = NULL; + + if (command == E_CONTENT_EDITOR_COMMAND_BOLD) + element_name = "b"; + else if (command == E_CONTENT_EDITOR_COMMAND_ITALIC) + element_name = "i"; + else if (command == E_CONTENT_EDITOR_COMMAND_UNDERLINE) + element_name = "u"; + else if (command == E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH) + element_name = "strike"; + + if (element_name) + set_font_style (e_editor_page_get_document (editor_page), element_name, value); + e_editor_dom_selection_restore (editor_page); + + goto exit; + } + e_editor_dom_selection_restore (editor_page); + + e_editor_dom_exec_command (editor_page, command, NULL); +exit: + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); +} + +/* + * e_html_editor_selection_set_underline: + * @selection: an #EEditorSelection + * @underline: @TRUE to enable underline, @FALSE to disable + * + * Toggles underline formatting of current selection or letter at current + * cursor position, depending on whether @underline is @TRUE or @FALSE. + */ +void +e_editor_dom_selection_set_underline (EEditorPage *editor_page, + gboolean underline) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_dom_selection_is_underline (editor_page) == underline) + return; + + selection_set_font_style ( + editor_page, E_CONTENT_EDITOR_COMMAND_UNDERLINE, underline); +} + +static gboolean +is_subscript_element (WebKitDOMElement *element) +{ + if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + return element_has_tag (element, "sub"); +} + +/* + * e_html_editor_selection_is_subscript: + * @selection: an #EEditorSelection + * + * Returns whether current selection or letter at current cursor position + * is in subscript. + * + * Returns @TRUE when selection is in subscript, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_subscript (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return dom_selection_is_font_format ( + editor_page, (IsRightFormatNodeFunc) is_subscript_element, NULL); +} + +/* + * e_html_editor_selection_set_subscript: + * @selection: an #EEditorSelection + * @subscript: @TRUE to enable subscript, @FALSE to disable + * + * Toggles subscript of current selection or letter at current cursor position, + * depending on whether @subscript is @TRUE or @FALSE. + */ +void +e_editor_dom_selection_set_subscript (EEditorPage *editor_page, + gboolean subscript) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_dom_selection_is_subscript (editor_page) == subscript) + return; + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_SUBSCRIPT, NULL); +} + +static gboolean +is_superscript_element (WebKitDOMElement *element) +{ + if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + return element_has_tag (element, "sup"); +} + +/* + * e_html_editor_selection_is_superscript: + * @selection: an #EEditorSelection + * + * Returns whether current selection or letter at current cursor position + * is in superscript. + * + * Returns @TRUE when selection is in superscript, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_superscript (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return dom_selection_is_font_format ( + editor_page, (IsRightFormatNodeFunc) is_superscript_element, NULL); +} + +/* + * e_html_editor_selection_set_superscript: + * @selection: an #EEditorSelection + * @superscript: @TRUE to enable superscript, @FALSE to disable + * + * Toggles superscript of current selection or letter at current cursor position, + * depending on whether @superscript is @TRUE or @FALSE. + */ +void +e_editor_dom_selection_set_superscript (EEditorPage *editor_page, + gboolean superscript) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_dom_selection_is_superscript (editor_page) == superscript) + return; + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT, NULL); +} + +static gboolean +is_strikethrough_element (WebKitDOMElement *element) +{ + if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + return element_has_tag (element, "strike"); +} + +/* + * e_html_editor_selection_is_strikethrough: + * @selection: an #EEditorSelection + * + * Returns whether current selection or letter at current cursor position + * is striked through. + * + * Returns @TRUE when selection is striked through, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_strikethrough (EEditorPage *editor_page) +{ + gboolean is_strikethrough; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + is_strikethrough = e_editor_page_get_strikethrough (editor_page); + is_strikethrough = dom_selection_is_font_format ( + editor_page, (IsRightFormatNodeFunc) is_strikethrough_element, &is_strikethrough); + + return is_strikethrough; +} + +/* + * e_html_editor_selection_set_strikethrough: + * @selection: an #EEditorSelection + * @strikethrough: @TRUE to enable strikethrough, @FALSE to disable + * + * Toggles strike through formatting of current selection or letter at current + * cursor position, depending on whether @strikethrough is @TRUE or @FALSE. + */ +void +e_editor_dom_selection_set_strikethrough (EEditorPage *editor_page, + gboolean strikethrough) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_dom_selection_is_strikethrough (editor_page) == strikethrough) + return; + + selection_set_font_style ( + editor_page, E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH, strikethrough); +} + +static gboolean +is_monospace_element (WebKitDOMElement *element) +{ + gchar *value; + gboolean ret_val = FALSE; + + if (!element) + return FALSE; + + if (!WEBKIT_DOM_IS_HTML_FONT_ELEMENT (element)) + return FALSE; + + value = webkit_dom_element_get_attribute (element, "face"); + if (value && g_strcmp0 (value, "monospace") == 0) + ret_val = TRUE; + + g_free (value); + + return ret_val; +} + +/* + * e_html_editor_selection_is_monospaced: + * @selection: an #EEditorSelection + * + * Returns whether current selection or letter at current cursor position + * is monospaced. + * + * Returns @TRUE when selection is monospaced, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_monospace (EEditorPage *editor_page) +{ + gboolean is_monospace; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + is_monospace = e_editor_page_get_monospace (editor_page); + is_monospace = dom_selection_is_font_format ( + editor_page, (IsRightFormatNodeFunc) is_monospace_element, &is_monospace); + + return is_monospace; +} + +static void +monospace_selection (EEditorPage *editor_page, + WebKitDOMElement *monospace_element) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *sibling, *node, *monospace, *block; + WebKitDOMNodeList *list = NULL; + gboolean selection_end = FALSE; + gboolean first = TRUE; + gint length, ii; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + block = WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker))); + + monospace = WEBKIT_DOM_NODE (monospace_element); + node = WEBKIT_DOM_NODE (selection_start_marker); + /* Go through first block in selection. */ + while (block && node && !webkit_dom_node_is_same_node (block, node)) { + if (webkit_dom_node_get_next_sibling (node)) { + /* Prepare the monospaced element. */ + monospace = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + first ? monospace : webkit_dom_node_clone_node_with_error (monospace, FALSE, NULL), + first ? node : webkit_dom_node_get_next_sibling (node), + NULL); + } else + break; + + /* Move the nodes into monospaced element. */ + while (((sibling = webkit_dom_node_get_next_sibling (monospace)))) { + webkit_dom_node_append_child (monospace, sibling, NULL); + if (webkit_dom_node_is_same_node (WEBKIT_DOM_NODE (selection_end_marker), sibling)) { + selection_end = TRUE; + break; + } + } + + node = webkit_dom_node_get_parent_node (monospace); + first = FALSE; + } + + /* Just one block was selected. */ + if (selection_end) + goto out; + + /* Middle blocks (blocks not containing the end of the selection. */ + block = webkit_dom_node_get_next_sibling (block); + while (block && !selection_end) { + WebKitDOMNode *next_block; + + selection_end = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_end_marker)); + + if (selection_end) + break; + + next_block = webkit_dom_node_get_next_sibling (block); + + monospace = webkit_dom_node_insert_before ( + block, + webkit_dom_node_clone_node_with_error (monospace, FALSE, NULL), + webkit_dom_node_get_first_child (block), + NULL); + + while (((sibling = webkit_dom_node_get_next_sibling (monospace)))) + webkit_dom_node_append_child (monospace, sibling, NULL); + + block = next_block; + } + + /* Block containing the end of selection. */ + node = WEBKIT_DOM_NODE (selection_end_marker); + while (block && node && !webkit_dom_node_is_same_node (block, node)) { + monospace = webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + webkit_dom_node_clone_node_with_error (monospace, FALSE, NULL), + webkit_dom_node_get_next_sibling (node), + NULL); + + while (((sibling = webkit_dom_node_get_previous_sibling (monospace)))) { + webkit_dom_node_insert_before ( + monospace, + sibling, + webkit_dom_node_get_first_child (monospace), + NULL); + } + + node = webkit_dom_node_get_parent_node (monospace); + } + out: + /* Merge all the monospace elements inside other monospace elements. */ + list = webkit_dom_document_query_selector_all ( + document, "font[face=monospace] > font[face=monospace]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *item; + WebKitDOMNode *child; + + item = webkit_dom_node_list_item (list, ii); + while ((child = webkit_dom_node_get_first_child (item))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (item), + child, + item, + NULL); + } + remove_node (item); + g_object_unref (item); + } + g_clear_object (&list); + + /* Merge all the adjacent monospace elements. */ + list = webkit_dom_document_query_selector_all ( + document, "font[face=monospace] + font[face=monospace]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *item; + WebKitDOMNode *child; + + item = webkit_dom_node_list_item (list, ii); + /* The + CSS selector will return some false positives as it doesn't + * take text between elements into account so it will return this: + * <font face="monospace">xx</font>yy<font face="monospace">zz</font> + * as valid, but it isn't so we have to check if previous node + * is indeed element or not. */ + if (WEBKIT_DOM_IS_ELEMENT (webkit_dom_node_get_previous_sibling (item))) { + while ((child = webkit_dom_node_get_first_child (item))) { + webkit_dom_node_append_child ( + webkit_dom_node_get_previous_sibling (item), child, NULL); + } + remove_node (item); + } + g_object_unref (item); + } + g_clear_object (&list); + + e_editor_dom_selection_restore (editor_page); +} + +static void +unmonospace_selection (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker; + WebKitDOMElement *selection_end_marker; + WebKitDOMElement *selection_start_clone; + WebKitDOMElement *selection_end_clone; + WebKitDOMNode *sibling, *node; + WebKitDOMNode *block, *clone, *monospace; + gboolean selection_end = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + block = WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker))); + + node = WEBKIT_DOM_NODE (selection_start_marker); + monospace = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); + while (monospace && !is_monospace_element (WEBKIT_DOM_ELEMENT (monospace))) + monospace = webkit_dom_node_get_parent_node (monospace); + + /* No monospaced element was found as a parent of selection start node. */ + if (!monospace) + goto out; + + /* Make a clone of current monospaced element. */ + clone = webkit_dom_node_clone_node_with_error (monospace, TRUE, NULL); + + /* First block */ + /* Remove all the nodes that are after the selection start point as they + * will be in the cloned node. */ + while (monospace && node && !webkit_dom_node_is_same_node (monospace, node)) { + WebKitDOMNode *tmp; + while (((sibling = webkit_dom_node_get_next_sibling (node)))) + remove_node (sibling); + + tmp = webkit_dom_node_get_parent_node (node); + if (webkit_dom_node_get_next_sibling (node)) + remove_node (node); + node = tmp; + } + + selection_start_clone = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (clone), "#-x-evo-selection-start-marker", NULL); + selection_end_clone = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (clone), "#-x-evo-selection-end-marker", NULL); + + /* No selection start node in the block where it is supposed to be, return. */ + if (!selection_start_clone) + goto out; + + /* Remove all the nodes until we hit the selection start point as these + * nodes will stay monospaced and they are already in original element. */ + node = webkit_dom_node_get_first_child (clone); + while (node) { + WebKitDOMNode *next_sibling; + + next_sibling = webkit_dom_node_get_next_sibling (node); + if (webkit_dom_node_get_first_child (node)) { + if (webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_start_clone))) { + node = webkit_dom_node_get_first_child (node); + continue; + } else + remove_node (node); + } else if (webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_start_clone))) + break; + else + remove_node (node); + + node = next_sibling; + } + + /* Insert the clone into the tree. Do it after the previous clean up. If + * we would do it the other way the line would contain duplicated text nodes + * and the block would be expading and shrinking while we would modify it. */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (monospace), + clone, + webkit_dom_node_get_next_sibling (monospace), + NULL); + + /* Move selection start point the right place. */ + remove_node (WEBKIT_DOM_NODE (selection_start_marker)); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (clone), + WEBKIT_DOM_NODE (selection_start_clone), + clone, + NULL); + + /* Move all the nodes the are supposed to lose the monospace formatting + * out of monospaced element. */ + node = webkit_dom_node_get_first_child (clone); + while (node) { + WebKitDOMNode *next_sibling; + + next_sibling = webkit_dom_node_get_next_sibling (node); + if (webkit_dom_node_get_first_child (node)) { + if (selection_end_clone && + webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_end_clone))) { + node = webkit_dom_node_get_first_child (node); + continue; + } else + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (clone), + node, + clone, + NULL); + } else if (selection_end_clone && + webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_end_clone))) { + selection_end = TRUE; + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (clone), + node, + clone, + NULL); + break; + } else + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (clone), + node, + clone, + NULL); + + node = next_sibling; + } + + if (!webkit_dom_node_get_first_child (clone)) + remove_node (clone); + + /* Just one block was selected and we hit the selection end point. */ + if (selection_end) + goto out; + + /* Middle blocks */ + block = webkit_dom_node_get_next_sibling (block); + while (block && !selection_end) { + WebKitDOMNode *next_block, *child, *parent; + WebKitDOMElement *monospace_element; + + selection_end = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_end_marker)); + + if (selection_end) + break; + + next_block = webkit_dom_node_get_next_sibling (block); + + /* Find the monospaced element and move all the nodes from it and + * finally remove it. */ + monospace_element = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block), "font[face=monospace]", NULL); + if (!monospace_element) + break; + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (monospace_element)); + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (monospace_element)))) { + webkit_dom_node_insert_before ( + parent, child, WEBKIT_DOM_NODE (monospace_element), NULL); + } + + remove_node (WEBKIT_DOM_NODE (monospace_element)); + + block = next_block; + } + + /* End block */ + node = WEBKIT_DOM_NODE (selection_end_marker); + monospace = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_end_marker)); + while (monospace && !is_monospace_element (WEBKIT_DOM_ELEMENT (monospace))) + monospace = webkit_dom_node_get_parent_node (monospace); + + /* No monospaced element was found as a parent of selection end node. */ + if (!monospace) + return; + + clone = WEBKIT_DOM_NODE (monospace); + node = webkit_dom_node_get_first_child (clone); + /* Move all the nodes that are supposed to lose the monospaced formatting + * out of the monospaced element. */ + while (node) { + WebKitDOMNode *next_sibling; + + next_sibling = webkit_dom_node_get_next_sibling (node); + if (webkit_dom_node_get_first_child (node)) { + if (webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_end_marker))) { + node = webkit_dom_node_get_first_child (node); + continue; + } else + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (clone), + node, + clone, + NULL); + } else if (webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_end_marker))) { + selection_end = TRUE; + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (clone), + node, + clone, + NULL); + break; + } else { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (clone), + node, + clone, + NULL); + } + + node = next_sibling; + } + + if (!webkit_dom_node_get_first_child (clone)) + remove_node (clone); + out: + e_editor_dom_selection_restore (editor_page); +} + +/* + * e_html_editor_selection_set_monospaced: + * @selection: an #EEditorSelection + * @monospaced: @TRUE to enable monospaced, @FALSE to disable + * + * Toggles monospaced formatting of current selection or letter at current cursor + * position, depending on whether @monospaced is @TRUE or @FALSE. + */ +void +e_editor_dom_selection_set_monospace (EEditorPage *editor_page, + gboolean value) +{ + WebKitDOMDocument *document; + WebKitDOMRange *range = NULL; + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + guint font_size = 0; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if ((e_editor_dom_selection_is_monospace (editor_page) ? 1 : 0) == (value ? 1 : 0)) + return; + + document = e_editor_page_get_document (editor_page); + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_MONOSPACE; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->data.style.from = !value; + ev->data.style.to = value; + } + + font_size = e_editor_page_get_font_size (editor_page); + if (font_size == 0) + font_size = E_CONTENT_EDITOR_FONT_SIZE_NORMAL; + + if (value) { + WebKitDOMElement *monospace; + + monospace = webkit_dom_document_create_element ( + document, "font", NULL); + webkit_dom_element_set_attribute ( + monospace, "face", "monospace", NULL); + if (font_size != 0) { + gchar *font_size_str; + + font_size_str = g_strdup_printf ("%d", font_size); + webkit_dom_element_set_attribute ( + monospace, "size", font_size_str, NULL); + g_free (font_size_str); + } + + if (!webkit_dom_range_get_collapsed (range, NULL)) + monospace_selection (editor_page, monospace); + else { + /* https://bugs.webkit.org/show_bug.cgi?id=15256 */ + webkit_dom_element_set_inner_html ( + monospace, + UNICODE_ZERO_WIDTH_SPACE, + NULL); + webkit_dom_range_insert_node ( + range, WEBKIT_DOM_NODE (monospace), NULL); + + e_editor_dom_move_caret_into_element (editor_page, monospace, FALSE); + } + } else { + gboolean is_bold = FALSE, is_italic = FALSE; + gboolean is_underline = FALSE, is_strikethrough = FALSE; + guint font_size = 0; + WebKitDOMElement *tt_element; + WebKitDOMNode *node; + + node = webkit_dom_range_get_end_container (range, NULL); + if (WEBKIT_DOM_IS_ELEMENT (node) && + is_monospace_element (WEBKIT_DOM_ELEMENT (node))) { + tt_element = WEBKIT_DOM_ELEMENT (node); + } else { + tt_element = dom_node_find_parent_element (node, "FONT"); + + if (!is_monospace_element (tt_element)) { + g_clear_object (&range); + g_free (ev); + return; + } + } + + /* Save current formatting */ + is_bold = e_editor_page_get_bold (editor_page); + is_italic = e_editor_page_get_italic (editor_page); + is_underline = e_editor_page_get_underline (editor_page); + is_strikethrough = e_editor_page_get_strikethrough (editor_page); + + if (!e_editor_dom_selection_is_collapsed (editor_page)) + unmonospace_selection (editor_page); + else { + e_editor_dom_selection_save (editor_page); + set_font_style (document, "", FALSE); + e_editor_dom_selection_restore (editor_page); + } + + /* Re-set formatting */ + if (is_bold) + e_editor_dom_selection_set_bold (editor_page, TRUE); + if (is_italic) + e_editor_dom_selection_set_italic (editor_page, TRUE); + if (is_underline) + e_editor_dom_selection_set_underline (editor_page, TRUE); + if (is_strikethrough) + e_editor_dom_selection_set_strikethrough (editor_page, TRUE); + + if (font_size) + e_editor_dom_selection_set_font_size (editor_page, font_size); + } + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + + g_clear_object (&range); +} + +static gboolean +is_bold_element (WebKitDOMElement *element) +{ + if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + if (element_has_tag (element, "b")) + return TRUE; + + /* Headings are bold by default */ + return WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (element); +} + +/* + * e_html_editor_selection_is_bold: + * @selection: an #EEditorSelection + * + * Returns whether current selection or letter at current cursor position + * is bold. + * + * Returns @TRUE when selection is bold, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_bold (EEditorPage *editor_page) +{ + gboolean is_bold; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + is_bold = e_editor_page_get_bold (editor_page); + + is_bold = dom_selection_is_font_format ( + editor_page, (IsRightFormatNodeFunc) is_bold_element, &is_bold); + + return is_bold; +} + +/* + * e_html_editor_selection_set_bold: + * @selection: an #EEditorSelection + * @bold: @TRUE to enable bold, @FALSE to disable + * + * Toggles bold formatting of current selection or letter at current cursor + * position, depending on whether @bold is @TRUE or @FALSE. + */ +void +e_editor_dom_selection_set_bold (EEditorPage *editor_page, + gboolean bold) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_dom_selection_is_bold (editor_page) == bold) + return; + + selection_set_font_style ( + editor_page, E_CONTENT_EDITOR_COMMAND_BOLD, bold); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); +} + +static gboolean +is_italic_element (WebKitDOMElement *element) +{ + if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + return element_has_tag (element, "i") || element_has_tag (element, "address"); +} + +/* + * e_html_editor_selection_is_italic: + * @selection: an #EEditorSelection + * + * Returns whether current selection or letter at current cursor position + * is italic. + * + * Returns @TRUE when selection is italic, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_italic (EEditorPage *editor_page) +{ + gboolean is_italic; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + is_italic = e_editor_page_get_italic (editor_page); + is_italic = dom_selection_is_font_format ( + editor_page, (IsRightFormatNodeFunc) is_italic_element, &is_italic); + + return is_italic; +} + +/* + * e_html_editor_selection_set_italic: + * @selection: an #EEditorSelection + * @italic: @TRUE to enable italic, @FALSE to disable + * + * Toggles italic formatting of current selection or letter at current cursor + * position, depending on whether @italic is @TRUE or @FALSE. + */ +void +e_editor_dom_selection_set_italic (EEditorPage *editor_page, + gboolean italic) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_dom_selection_is_italic (editor_page) == italic) + return; + + selection_set_font_style ( + editor_page, E_CONTENT_EDITOR_COMMAND_ITALIC, italic); +} + +/* + * e_html_editor_selection_is_indented: + * @selection: an #EEditorSelection + * + * Returns whether current paragraph is indented. This does not include + * citations. To check, whether paragraph is a citation, use + * e_html_editor_selection_is_citation(). + * + * Returns: @TRUE when current paragraph is indented, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_indented (EEditorPage *editor_page) +{ + WebKitDOMElement *element; + WebKitDOMRange *range = NULL; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return FALSE; + + if (webkit_dom_range_get_collapsed (range, NULL)) { + element = get_element_for_inspection (range); + g_clear_object (&range); + return element_has_class (element, "-x-evo-indented"); + } else { + WebKitDOMNode *node; + gboolean ret_val; + + node = webkit_dom_range_get_end_container (range, NULL); + /* No selection or whole body selected */ + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) + goto out; + + element = WEBKIT_DOM_ELEMENT (get_parent_indented_block (node)); + ret_val = element_has_class (element, "-x-evo-indented"); + if (!ret_val) + goto out; + + node = webkit_dom_range_get_start_container (range, NULL); + /* No selection or whole body selected */ + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) + goto out; + + element = WEBKIT_DOM_ELEMENT (get_parent_indented_block (node)); + ret_val = element_has_class (element, "-x-evo-indented"); + + g_clear_object (&range); + + return ret_val; + } + + out: + g_clear_object (&range); + + return FALSE; +} + +/* + * e_html_editor_selection_is_citation: + * @selection: an #EEditorSelection + * + * Returns whether current paragraph is a citation. + * + * Returns: @TRUE when current paragraph is a citation, @FALSE otherwise. + */ +gboolean +e_editor_dom_selection_is_citation (EEditorPage *editor_page) +{ + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + gboolean ret_val; + gchar *value, *text_content; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return FALSE; + + node = webkit_dom_range_get_common_ancestor_container (range, NULL); + g_clear_object (&range); + + if (WEBKIT_DOM_IS_TEXT (node)) + return get_has_style (editor_page, "citation"); + + text_content = webkit_dom_node_get_text_content (node); + if (g_strcmp0 (text_content, "") == 0) { + g_free (text_content); + return FALSE; + } + g_free (text_content); + + value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type"); + /* citation == <blockquote type='cite'> */ + if (value && strstr (value, "cite")) + ret_val = TRUE; + else + ret_val = get_has_style (editor_page, "citation"); + + g_free (value); + return ret_val; +} + +static gchar * +get_font_property (EEditorPage *editor_page, + const gchar *font_property) +{ + WebKitDOMRange *range = NULL; + WebKitDOMNode *node; + WebKitDOMElement *element; + gchar *value; + + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return NULL; + + node = webkit_dom_range_get_common_ancestor_container (range, NULL); + g_clear_object (&range); + element = dom_node_find_parent_element (node, "FONT"); + while (element && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (element) && + !webkit_dom_element_has_attribute (element, font_property)) { + element = dom_node_find_parent_element ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), "FONT"); + } + + if (!element) + return NULL; + + g_object_get (G_OBJECT (element), font_property, &value, NULL); + + return value; +} + +/* + * e_editor_dom_selection_get_font_size: + * @selection: an #EEditorSelection + * + * Returns point size of current selection or of letter at current cursor position. + */ +guint +e_editor_dom_selection_get_font_size (EEditorPage *editor_page) +{ + gchar *size; + guint size_int; + gboolean increment; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + size = get_font_property (editor_page, "size"); + if (!(size && *size)) { + g_free (size); + return E_CONTENT_EDITOR_FONT_SIZE_NORMAL; + } + + /* We don't support increments, but when going through a content that + * was not written in Evolution we can find it. In this case just report + * the normal size. */ + /* FIXME: go through all parent and get the right value. */ + increment = size[0] == '+' || size[0] == '-'; + size_int = atoi (size); + g_free (size); + + if (increment || size_int == 0) + return E_CONTENT_EDITOR_FONT_SIZE_NORMAL; + + return size_int; +} + +/* + * e_html_editor_selection_set_font_size: + * @selection: an #EEditorSelection + * @font_size: point size to apply + * + * Sets font size of current selection or of letter at current cursor position + * to @font_size. + */ +void +e_editor_dom_selection_set_font_size (EEditorPage *editor_page, + EContentEditorFontSize font_size) +{ + WebKitDOMDocument *document; + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + gchar *size_str; + guint current_font_size; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + current_font_size = e_editor_dom_selection_get_font_size (editor_page); + if (current_font_size == font_size) + return; + + e_editor_dom_selection_save (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_FONT_SIZE; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->data.style.from = current_font_size; + ev->data.style.to = font_size; + } + + size_str = g_strdup_printf ("%d", font_size); + + if (e_editor_dom_selection_is_collapsed (editor_page)) { + WebKitDOMElement *font; + + font = set_font_style (document, "font", font_size != 3); + if (font) + webkit_dom_element_set_attribute (font, "size", size_str, NULL); + e_editor_dom_selection_restore (editor_page); + goto exit; + } + + e_editor_dom_selection_restore (editor_page); + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_FONT_SIZE, size_str); + + /* Text in <font size="3"></font> (size 3 is our default size) is a little + * bit smaller than font outsize it. So move it outside of it. */ + if (font_size == E_CONTENT_EDITOR_FONT_SIZE_NORMAL) { + WebKitDOMElement *element; + + element = webkit_dom_document_query_selector (document, "font[size=\"3\"]", NULL); + if (element) { + WebKitDOMNode *child; + + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + child, + WEBKIT_DOM_NODE (element), + NULL); + + remove_node (WEBKIT_DOM_NODE (element)); + } + } + + exit: + g_free (size_str); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } +} + +/* + * e_html_editor_selection_set_font_name: + * @selection: an #EEditorSelection + * @font_name: a font name to apply + * + * Sets font name of current selection or of letter at current cursor position + * to @font_name. + */ +void +e_editor_dom_selection_set_font_name (EEditorPage *editor_page, + const gchar *font_name) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_FONT_NAME, font_name); +} + +/* + * e_html_editor_selection_get_font_name: + * @selection: an #EEditorSelection + * + * Returns name of font used in current selection or at letter at current cursor + * position. + * + * Returns: A string with font name. [transfer-none] + */ +gchar * +e_editor_dom_selection_get_font_name (EEditorPage *editor_page) +{ + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + WebKitDOMCSSStyleDeclaration *css = NULL; + gchar *value; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + range = e_editor_dom_get_current_range (editor_page); + node = webkit_dom_range_get_common_ancestor_container (range, NULL); + g_clear_object (&range); + + css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node)); + value = webkit_dom_css_style_declaration_get_property_value (css, "fontFamily"); + g_clear_object (&css); + + return value; +} + +/* + * e_html_editor_selection_set_font_color: + * @selection: an #EEditorSelection + * @rgba: a #GdkRGBA + * + * Sets font color of current selection or letter at current cursor position to + * color defined in @rgba. + */ +void +e_editor_dom_selection_set_font_color (EEditorPage *editor_page, + const gchar *color) +{ + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_FONT_COLOR; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->data.string.from = g_strdup (e_editor_page_get_font_color (editor_page)); + ev->data.string.to = g_strdup (color); + } + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_FORE_COLOR, color); + + if (ev) { + ev->after.start.x = ev->before.start.x; + ev->after.start.y = ev->before.start.y; + ev->after.end.x = ev->before.end.x; + ev->after.end.y = ev->before.end.y; + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } +} + +/* + * e_html_editor_selection_get_font_color: + * @selection: an #EEditorSelection + * @rgba: a #GdkRGBA object to be set to current font color + * + * Sets @rgba to contain color of current text selection or letter at current + * cursor position. + */ +gchar * +e_editor_dom_selection_get_font_color (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + gchar *color; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + color = get_font_property (editor_page, "color"); + if (!(color && *color)) { + WebKitDOMHTMLElement *body; + + body = webkit_dom_document_get_body (document); + g_free (color); + color = webkit_dom_html_body_element_get_text (WEBKIT_DOM_HTML_BODY_ELEMENT (body)); + if (!(color && *color)) { + g_free (color); + return g_strdup ("#000000"); + } + } + + return color; +} + +/* + * e_html_editor_selection_get_block_format: + * @selection: an #EEditorSelection + * + * Returns block format of current paragraph. + * + * Returns: #EContentEditorBlockFormat + */ +EContentEditorBlockFormat +e_editor_dom_selection_get_block_format (EEditorPage *editor_page) +{ + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + WebKitDOMElement *element; + EContentEditorBlockFormat result; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_CONTENT_EDITOR_BLOCK_FORMAT_NONE); + + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH; + + node = webkit_dom_range_get_start_container (range, NULL); + + if ((element = dom_node_find_parent_element (node, "UL"))) { + WebKitDOMElement *tmp_element; + + tmp_element = dom_node_find_parent_element (node, "OL"); + if (tmp_element) { + if (webkit_dom_node_contains (WEBKIT_DOM_NODE (tmp_element), WEBKIT_DOM_NODE (element))) + result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (element)); + else + result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element)); + } else + result = E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST; + } else if ((element = dom_node_find_parent_element (node, "OL")) != NULL) { + WebKitDOMElement *tmp_element; + + tmp_element = dom_node_find_parent_element (node, "UL"); + if (tmp_element) { + if (webkit_dom_node_contains (WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (tmp_element))) + result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (element)); + else + result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element)); + } else + result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (element)); + } else if (dom_node_find_parent_element (node, "PRE")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_PRE; + } else if (dom_node_find_parent_element (node, "ADDRESS")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS; + } else if (dom_node_find_parent_element (node, "H1")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_H1; + } else if (dom_node_find_parent_element (node, "H2")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_H2; + } else if (dom_node_find_parent_element (node, "H3")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_H3; + } else if (dom_node_find_parent_element (node, "H4")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_H4; + } else if (dom_node_find_parent_element (node, "H5")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_H5; + } else if (dom_node_find_parent_element (node, "H6")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_H6; + } else if ((element = dom_node_find_parent_element (node, "BLOCKQUOTE")) != NULL) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH; + } else if (dom_node_find_parent_element (node, "P")) { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH; + } else { + result = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH; + } + + g_clear_object (&range); + + return result; +} + +static void +change_leading_space_to_nbsp (WebKitDOMNode *block) +{ + WebKitDOMNode *child; + + if (!WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block)) + return; + + if ((child = webkit_dom_node_get_first_child (block)) && + WEBKIT_DOM_IS_CHARACTER_DATA (child)) { + gchar *data; + + data = webkit_dom_character_data_substring_data ( + WEBKIT_DOM_CHARACTER_DATA (child), 0, 1, NULL); + + if (data && *data == ' ') + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (child), 0, 1, UNICODE_NBSP, NULL); + g_free (data); + } +} + +static void +change_trailing_space_in_block_to_nbsp (WebKitDOMNode *block) +{ + WebKitDOMNode *child; + + if ((child = webkit_dom_node_get_last_child (block)) && + WEBKIT_DOM_IS_CHARACTER_DATA (child)) { + gchar *tmp; + gulong length; + + length = webkit_dom_character_data_get_length ( + WEBKIT_DOM_CHARACTER_DATA (child)); + + tmp = webkit_dom_character_data_substring_data ( + WEBKIT_DOM_CHARACTER_DATA (child), length - 1, 1, NULL); + if (tmp && *tmp == ' ') { + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (child), + length - 1, + 1, + UNICODE_NBSP, + NULL); + } + g_free (tmp); + } +} + +static void +change_space_before_selection_to_nbsp (WebKitDOMNode *node) +{ + WebKitDOMNode *prev_sibling; + + if ((prev_sibling = webkit_dom_node_get_previous_sibling (node))) { + if (WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling)) { + gchar *tmp; + gulong length; + + length = webkit_dom_character_data_get_length ( + WEBKIT_DOM_CHARACTER_DATA (prev_sibling)); + + tmp = webkit_dom_character_data_substring_data ( + WEBKIT_DOM_CHARACTER_DATA (prev_sibling), length - 1, 1, NULL); + if (tmp && *tmp == ' ') { + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (prev_sibling), + length - 1, + 1, + UNICODE_NBSP, + NULL); + } + g_free (tmp); + } + } +} + +static gboolean +process_block_to_block (EEditorPage *editor_page, + EContentEditorBlockFormat format, + const gchar *value, + WebKitDOMNode *block, + WebKitDOMNode *end_block, + WebKitDOMNode *blockquote, + gboolean html_mode) +{ + WebKitDOMDocument *document; + WebKitDOMNode *next_block; + gboolean after_selection_end = FALSE; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + document = e_editor_page_get_document (editor_page); + + while (!after_selection_end && block) { + gboolean quoted = FALSE; + gboolean empty = FALSE; + gchar *content; + gint citation_level = 0; + WebKitDOMNode *child; + WebKitDOMElement *element; + + if (e_editor_dom_node_is_citation_node (block)) { + gboolean finished; + + next_block = webkit_dom_node_get_next_sibling (block); + finished = process_block_to_block ( + editor_page, + format, + value, + webkit_dom_node_get_first_child (block), + end_block, + blockquote, + html_mode); + + if (finished) + return TRUE; + + block = next_block; + + continue; + } + + if (webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) { + quoted = TRUE; + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block)); + } + + if (!html_mode) + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block)); + + after_selection_end = webkit_dom_node_is_same_node (block, end_block); + + next_block = webkit_dom_node_get_next_sibling (block); + + if (node_is_list (block)) { + WebKitDOMNode *item; + + item = webkit_dom_node_get_first_child (block); + while (item && !WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) + item = webkit_dom_node_get_first_child (item); + + if (item && do_format_change_list_to_block (editor_page, format, item, value)) + return TRUE; + + block = next_block; + + continue; + } + + if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH) + element = e_editor_dom_get_paragraph_element (editor_page, -1, 0); + else + element = webkit_dom_document_create_element ( + document, value, NULL); + + content = webkit_dom_node_get_text_content (block); + + empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0); + g_free (content); + + change_leading_space_to_nbsp (block); + change_trailing_space_in_block_to_nbsp (block); + + while ((child = webkit_dom_node_get_first_child (block))) { + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)) + empty = FALSE; + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), child, NULL); + } + + if (empty) { + WebKitDOMElement *br; + + br = webkit_dom_document_create_element ( + document, "BR", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (br), NULL); + } + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + WEBKIT_DOM_NODE (element), + block, + NULL); + + remove_node (block); + + if (!next_block && !after_selection_end) { + citation_level = selection_get_citation_level (WEBKIT_DOM_NODE (element)); + + if (citation_level > 0) { + next_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + next_block = webkit_dom_node_get_next_sibling (next_block); + } + } + + block = next_block; + + if (!html_mode && format == E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH) { + citation_level = selection_get_citation_level (WEBKIT_DOM_NODE (element)); + + if (citation_level > 0) { + gint quote, word_wrap_length; + + word_wrap_length = + e_editor_page_get_word_wrap_length (editor_page); + quote = citation_level ? citation_level * 2 : 0; + + element = e_editor_dom_wrap_paragraph_length ( + editor_page, element, word_wrap_length - quote); + + } + } + + if (!html_mode && quoted) { + if (citation_level > 0) + e_editor_dom_quote_plain_text_element_after_wrapping ( + editor_page, element, citation_level); + else + e_editor_dom_quote_plain_text_element (editor_page, element); + } + } + + return after_selection_end; +} + +static void +format_change_block_to_block (EEditorPage *editor_page, + EContentEditorBlockFormat format, + const gchar *value) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *block, *end_block, *blockquote = NULL; + gboolean html_mode = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + /* If the selection was not saved, move it into the first child of body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMHTMLElement *body; + WebKitDOMNode *child; + + body = webkit_dom_document_get_body (document); + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + dom_add_selection_markers_into_element_start ( + document, + WEBKIT_DOM_ELEMENT (child), + &selection_start_marker, + &selection_end_marker); + } + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + html_mode = e_editor_page_get_html_mode (editor_page); + + end_block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_end_marker)); + + /* Process all blocks that are in the selection one by one */ + process_block_to_block ( + editor_page, format, value, block, end_block, blockquote, html_mode); +} + +static void +format_change_block_to_list (EEditorPage *editor_page, + EContentEditorBlockFormat format) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker, *item, *list; + WebKitDOMNode *block, *next_block; + gboolean after_selection_end = FALSE, in_quote = FALSE; + gboolean html_mode = e_editor_page_get_html_mode (editor_page); + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + /* If the selection was not saved, move it into the first child of body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMHTMLElement *body; + WebKitDOMNode *child; + + body = webkit_dom_document_get_body (document); + child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)); + + dom_add_selection_markers_into_element_start ( + document, + WEBKIT_DOM_ELEMENT (child), + &selection_start_marker, + &selection_end_marker); + } + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + list = create_list_element (editor_page, format, 0, html_mode); + + if (webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) { + WebKitDOMElement *element; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + + in_quote = TRUE; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + range = webkit_dom_document_create_range (document); + + webkit_dom_range_select_node (range, block, NULL); + webkit_dom_range_collapse (range, TRUE, NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + + g_clear_object (&range); + g_clear_object (&dom_selection); + g_clear_object (&dom_window); + + e_editor_dom_remove_input_event_listener_from_body (editor_page); + e_editor_page_block_selection_changed (editor_page); + + e_editor_dom_exec_command ( + editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL); + + e_editor_dom_register_input_event_listener_on_body (editor_page); + e_editor_page_unblock_selection_changed (editor_page); + + element = webkit_dom_document_query_selector ( + document, "body>br", NULL); + + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + WEBKIT_DOM_NODE (list), + WEBKIT_DOM_NODE (element), + NULL); + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + } else + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + WEBKIT_DOM_NODE (list), + block, + NULL); + + /* Process all blocks that are in the selection one by one */ + while (block && !after_selection_end) { + gboolean empty = FALSE, block_is_list; + gchar *content; + WebKitDOMNode *child, *parent; + + after_selection_end = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_end_marker)); + + next_block = webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (block)); + + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block)); + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block)); + + item = webkit_dom_document_create_element (document, "LI", NULL); + content = webkit_dom_node_get_text_content (block); + + empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0); + g_free (content); + + change_leading_space_to_nbsp (block); + change_trailing_space_in_block_to_nbsp (block); + + block_is_list = node_is_list_or_item (block); + + while ((child = webkit_dom_node_get_first_child (block))) { + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)) + empty = FALSE; + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (block_is_list ? list : item), child, NULL); + } + + if (!block_is_list) { + /* We have to use again the hidden space to move caret into newly inserted list */ + if (empty) { + WebKitDOMElement *br; + + br = webkit_dom_document_create_element ( + document, "BR", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (item), WEBKIT_DOM_NODE (br), NULL); + } + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (list), WEBKIT_DOM_NODE (item), NULL); + } + + parent = webkit_dom_node_get_parent_node (block); + remove_node (block); + + if (in_quote) { + /* Remove all parents if previously removed node was the + * only one with text content */ + content = webkit_dom_node_get_text_content (parent); + while (parent && content && !*content) { + WebKitDOMNode *tmp = webkit_dom_node_get_parent_node (parent); + + remove_node (parent); + parent = tmp; + + g_free (content); + content = webkit_dom_node_get_text_content (parent); + } + g_free (content); + } + + block = next_block; + } + + merge_lists_if_possible (WEBKIT_DOM_NODE (list)); +} + +static WebKitDOMElement * +do_format_change_list_to_list (WebKitDOMElement *list_to_process, + WebKitDOMElement *new_list_template, + EContentEditorBlockFormat to) +{ + EContentEditorBlockFormat current_format; + + current_format = dom_get_list_format_from_node ( + WEBKIT_DOM_NODE (list_to_process)); + if (to == current_format) { + /* Same format, skip it. */ + return list_to_process; + } else if (current_format >= E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST && + to >= E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST) { + /* Changing from ordered list type to another ordered list type. */ + set_ordered_list_type_to_element (list_to_process, to); + return list_to_process; + } else { + WebKitDOMNode *clone, *child; + + /* Create new list from template. */ + clone = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (new_list_template), FALSE, NULL); + + /* Insert it before the list that we are processing. */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (list_to_process)), + clone, + WEBKIT_DOM_NODE (list_to_process), + NULL); + + /* Move all it children to the new one. */ + while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (list_to_process)))) + webkit_dom_node_append_child (clone, child, NULL); + + remove_node (WEBKIT_DOM_NODE (list_to_process)); + + return WEBKIT_DOM_ELEMENT (clone); + } + + return NULL; +} + +static void +format_change_list_from_list (EEditorPage *editor_page, + EContentEditorBlockFormat to, + gboolean html_mode) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker, *new_list; + WebKitDOMNode *source_list, *source_list_clone, *current_list, *item; + gboolean after_selection_end = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + if (!selection_start_marker || !selection_end_marker) + return; + + /* Copy elements from previous block to list */ + item = get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start_marker)); + source_list = webkit_dom_node_get_parent_node (item); + current_list = source_list; + source_list_clone = webkit_dom_node_clone_node_with_error (source_list, FALSE, NULL); + + new_list = create_list_element (editor_page, to, 0, html_mode); + + if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented")) + element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented"); + + while (item) { + gboolean selection_end; + WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item); + + selection_end = webkit_dom_node_contains ( + item, WEBKIT_DOM_NODE (selection_end_marker)); + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) { + /* Actual node is an item, just copy it. */ + webkit_dom_node_append_child ( + after_selection_end ? + source_list_clone : WEBKIT_DOM_NODE (new_list), + item, + NULL); + } else if (node_is_list (item) && !selection_end && !after_selection_end) { + /* Node is a list and it doesn't contain the selection end + * marker, we can process the whole list. */ + gint ii; + WebKitDOMNodeList *list = NULL; + WebKitDOMElement *processed_list; + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL); + ii = webkit_dom_node_list_get_length (list); + g_clear_object (&list); + + /* Process every sublist separately. */ + while (ii) { + WebKitDOMElement *list_to_process; + + list_to_process = webkit_dom_element_query_selector ( + WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL); + if (list_to_process) + do_format_change_list_to_list (list_to_process, new_list, to); + ii--; + } + + /* Process the current list. */ + processed_list = do_format_change_list_to_list ( + WEBKIT_DOM_ELEMENT (item), new_list, to); + + webkit_dom_node_append_child ( + after_selection_end ? + source_list_clone : WEBKIT_DOM_NODE (new_list), + WEBKIT_DOM_NODE (processed_list), + NULL); + } else if (node_is_list (item) && !after_selection_end) { + /* Node is a list and it contains the selection end marker, + * thus we have to process it until we find the marker. */ + gint ii; + WebKitDOMNodeList *list = NULL; + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL); + ii = webkit_dom_node_list_get_length (list); + g_clear_object (&list); + + /* No nested lists - process the items. */ + if (ii == 0) { + WebKitDOMNode *clone, *child; + + clone = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (new_list), FALSE, NULL); + + webkit_dom_node_append_child ( + after_selection_end ? + source_list_clone : WEBKIT_DOM_NODE (new_list), + clone, + NULL); + + while ((child = webkit_dom_node_get_first_child (item))) { + webkit_dom_node_append_child (clone, child, NULL); + if (webkit_dom_node_contains (child, WEBKIT_DOM_NODE (selection_end_marker))) + break; + } + + if (webkit_dom_node_get_first_child (item)) + webkit_dom_node_append_child ( + after_selection_end ? + source_list_clone : WEBKIT_DOM_NODE (new_list), + item, + NULL); + else + remove_node (item); + } else { + gboolean done = FALSE; + WebKitDOMNode *tmp_parent = WEBKIT_DOM_NODE (new_list); + WebKitDOMNode *tmp_item = WEBKIT_DOM_NODE (item); + + while (!done) { + WebKitDOMNode *clone, *child; + + clone = webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (new_list), FALSE, NULL); + + webkit_dom_node_append_child ( + tmp_parent, clone, NULL); + + while ((child = webkit_dom_node_get_first_child (tmp_item))) { + if (!webkit_dom_node_contains (child, WEBKIT_DOM_NODE (selection_end_marker))) { + webkit_dom_node_append_child (clone, child, NULL); + } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (child)) { + webkit_dom_node_append_child (clone, child, NULL); + done = TRUE; + break; + } else { + tmp_parent = clone; + tmp_item = child; + break; + } + } + } + } + } else { + webkit_dom_node_append_child ( + after_selection_end ? + source_list_clone : WEBKIT_DOM_NODE (new_list), + item, + NULL); + } + + if (selection_end) { + source_list_clone = webkit_dom_node_clone_node_with_error (current_list, FALSE, NULL); + remove_node_if_empty (current_list); + after_selection_end = TRUE; + } + + if (!next_item) { + if (after_selection_end) + break; + + current_list = webkit_dom_node_get_next_sibling (current_list); + if (!node_is_list_or_item (current_list)) + break; + if (node_is_list (current_list)) { + next_item = webkit_dom_node_get_first_child (current_list); + if (!node_is_list_or_item (next_item)) + break; + } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (current_list)) { + next_item = current_list; + current_list = webkit_dom_node_get_parent_node (next_item); + } + } + + item = next_item; + } + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (source_list), + WEBKIT_DOM_NODE (source_list_clone), + webkit_dom_node_get_next_sibling (source_list), + NULL); + + if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (new_list))) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (source_list_clone), + WEBKIT_DOM_NODE (new_list), + source_list_clone, + NULL); + + remove_node_if_empty (source_list); + + remove_node_if_empty (source_list_clone); + + merge_lists_if_possible (WEBKIT_DOM_NODE (new_list)); +} + +static void +format_change_list_to_list (EEditorPage *editor_page, + EContentEditorBlockFormat format, + gboolean html_mode) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *prev_list, *current_list, *next_list; + EContentEditorBlockFormat prev = 0, next = 0; + gboolean done = FALSE, indented = FALSE; + gboolean selection_starts_in_first_child, selection_ends_in_last_child; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + current_list = get_list_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + prev_list = get_list_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + next_list = get_list_node_from_child ( + WEBKIT_DOM_NODE (selection_end_marker)); + + selection_starts_in_first_child = + webkit_dom_node_contains ( + webkit_dom_node_get_first_child (current_list), + WEBKIT_DOM_NODE (selection_start_marker)); + + selection_ends_in_last_child = + webkit_dom_node_contains ( + webkit_dom_node_get_last_child (current_list), + WEBKIT_DOM_NODE (selection_end_marker)); + + indented = element_has_class (WEBKIT_DOM_ELEMENT (current_list), "-x-evo-indented"); + + if (!prev_list || !next_list || indented) { + format_change_list_from_list (editor_page, format, html_mode); + return; + } + + if (webkit_dom_node_is_same_node (prev_list, next_list)) { + prev_list = webkit_dom_node_get_previous_sibling ( + webkit_dom_node_get_parent_node ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)))); + next_list = webkit_dom_node_get_next_sibling ( + webkit_dom_node_get_parent_node ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_end_marker)))); + if (!prev_list || !next_list) { + format_change_list_from_list (editor_page, format, html_mode); + return; + } + } + + prev = dom_get_list_format_from_node (prev_list); + next = dom_get_list_format_from_node (next_list); + + if (format != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) { + if (format == prev && prev != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) { + if (selection_starts_in_first_child && selection_ends_in_last_child) { + done = TRUE; + merge_list_into_list (current_list, prev_list, FALSE); + } + } + if (format == next && next != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) { + if (selection_starts_in_first_child && selection_ends_in_last_child) { + done = TRUE; + merge_list_into_list (next_list, prev_list, FALSE); + } + } + } + + if (done) + return; + + format_change_list_from_list (editor_page, format, html_mode); +} + +/* + * e_html_editor_selection_set_block_format: + * @selection: an #EEditorSelection + * @format: an #EContentEditorBlockFormat value + * + * Changes block format of current paragraph to @format. + */ +void +e_editor_dom_selection_set_block_format (EEditorPage *editor_page, + EContentEditorBlockFormat format) +{ + WebKitDOMDocument *document; + WebKitDOMRange *range = NULL; + EContentEditorBlockFormat current_format; + EContentEditorAlignment current_alignment; + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + const gchar *value; + gboolean from_list = FALSE, to_list = FALSE, html_mode = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + current_format = e_editor_dom_selection_get_block_format (editor_page); + if (current_format == format) + return; + + switch (format) { + case E_CONTENT_EDITOR_BLOCK_FORMAT_H1: + value = "H1"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_H2: + value = "H2"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_H3: + value = "H3"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_H4: + value = "H4"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_H5: + value = "H5"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_H6: + value = "H6"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH: + value = "P"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_PRE: + value = "PRE"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS: + value = "ADDRESS"; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST: + case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA: + case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN: + to_list = TRUE; + value = NULL; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST: + to_list = TRUE; + value = NULL; + break; + case E_CONTENT_EDITOR_BLOCK_FORMAT_NONE: + default: + value = NULL; + break; + } + + html_mode = e_editor_page_get_html_mode (editor_page); + + from_list = + current_format >= E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST; + + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return; + + current_alignment = e_editor_page_get_alignment (editor_page); + + e_editor_dom_selection_save (editor_page); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_BLOCK_FORMAT; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + ev->data.style.from = current_format; + ev->data.style.to = format; + } + + g_clear_object (&range); + + if (current_format == E_CONTENT_EDITOR_BLOCK_FORMAT_PRE) { + WebKitDOMElement *selection_marker; + + selection_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (selection_marker) + change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker)); + selection_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + if (selection_marker) + change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker)); + } + + if (from_list && to_list) + format_change_list_to_list (editor_page, format, html_mode); + + if (!from_list && !to_list) + format_change_block_to_block (editor_page, format, value); + + if (from_list && !to_list) + format_change_list_to_block (editor_page, format, value); + + if (!from_list && to_list) + format_change_block_to_list (editor_page, format); + + e_editor_dom_selection_restore (editor_page); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + + /* When changing the format we need to re-set the alignment */ + e_editor_dom_selection_set_alignment (editor_page, current_alignment); + + e_editor_page_emit_content_changed (editor_page); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } +} + +/* + * e_html_editor_selection_get_background_color: + * @selection: an #EEditorSelection + * + * Returns background color of currently selected text or letter at current + * cursor position. + * + * Returns: A string with code of current background color. + */ +gchar * +e_editor_dom_selection_get_background_color (EEditorPage *editor_page) +{ + WebKitDOMNode *ancestor; + WebKitDOMRange *range = NULL; + WebKitDOMCSSStyleDeclaration *css = NULL; + gchar *value; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + range = e_editor_dom_get_current_range (editor_page); + ancestor = webkit_dom_range_get_common_ancestor_container (range, NULL); + css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (ancestor)); +/* FIXME WK2 + g_free (selection->priv->background_color); + selection->priv->background_color = + webkit_dom_css_style_declaration_get_property_value ( + css, "background-color");*/ + + value = webkit_dom_css_style_declaration_get_property_value (css, "background-color"); + + g_clear_object (&css); + g_clear_object (&range); + + return value; +} + +/* + * e_html_editor_selection_set_background_color: + * @selection: an #EEditorSelection + * @color: code of new background color to set + * + * Changes background color of current selection or letter at current cursor + * position to @color. + */ +void +e_editor_dom_selection_set_background_color (EEditorPage *editor_page, + const gchar *color) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR, color); +} + +/* + * e_html_editor_selection_get_alignment: + * @selection: #an EEditorSelection + * + * Returns alignment of current paragraph + * + * Returns: #EContentEditorAlignment + */ +EContentEditorAlignment +e_editor_dom_selection_get_alignment (EEditorPage *editor_page) +{ + WebKitDOMCSSStyleDeclaration *style = NULL; + WebKitDOMElement *element; + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + EContentEditorAlignment alignment; + gchar *value; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_CONTENT_EDITOR_ALIGNMENT_LEFT); + + range = e_editor_dom_get_current_range (editor_page); + if (!range) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + goto out; + } + + node = webkit_dom_range_get_start_container (range, NULL); + g_clear_object (&range); + if (!node) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + goto out; + } + + if (WEBKIT_DOM_IS_ELEMENT (node)) + element = WEBKIT_DOM_ELEMENT (node); + else + element = webkit_dom_node_get_parent_element (node); + + if (element_has_class (element, "-x-evo-align-right")) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT; + goto out; + } else if (element_has_class (element, "-x-evo-align-center")) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER; + goto out; + } + + style = webkit_dom_element_get_style (element); + value = webkit_dom_css_style_declaration_get_property_value (style, "text-align"); + + if (!value || !*value || + (g_ascii_strncasecmp (value, "left", 4) == 0)) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + } else if (g_ascii_strncasecmp (value, "center", 6) == 0) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER; + } else if (g_ascii_strncasecmp (value, "right", 5) == 0) { + alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT; + } else { + alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + } + + g_clear_object (&style); + g_free (value); + + out: + return alignment; +} + +static void +set_block_alignment (WebKitDOMElement *element, + const gchar *class) +{ + WebKitDOMElement *parent; + + element_remove_class (element, "-x-evo-align-center"); + element_remove_class (element, "-x-evo-align-right"); + element_add_class (element, class); + parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element)); + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + element_remove_class (parent, "-x-evo-align-center"); + element_remove_class (parent, "-x-evo-align-right"); + parent = webkit_dom_node_get_parent_element ( + WEBKIT_DOM_NODE (parent)); + } +} + +/* + * e_html_editor_selection_set_alignment: + * @selection: an #EEditorSelection + * @alignment: an #EContentEditorAlignment value to apply + * + * Sets alignment of current paragraph to give @alignment. + */ +void +e_editor_dom_selection_set_alignment (EEditorPage *editor_page, + EContentEditorAlignment alignment) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *selection_end_marker; + WebKitDOMNode *block; + EContentEditorAlignment current_alignment; + EEditorUndoRedoManager *manager; + EEditorHistoryEvent *ev = NULL; + gboolean after_selection_end = FALSE; + const gchar *class = ""; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + current_alignment = e_editor_page_get_alignment (editor_page); + + if (current_alignment == alignment) + return; + + switch (alignment) { + case E_CONTENT_EDITOR_ALIGNMENT_CENTER: + class = "-x-evo-align-center"; + break; + + case E_CONTENT_EDITOR_ALIGNMENT_LEFT: + break; + + case E_CONTENT_EDITOR_ALIGNMENT_RIGHT: + class = "-x-evo-align-right"; + break; + } + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-start-marker", NULL); + selection_end_marker = webkit_dom_document_query_selector ( + document, "span#-x-evo-selection-end-marker", NULL); + + if (!selection_start_marker) + return; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_ALIGNMENT; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + ev->data.style.from = current_alignment; + ev->data.style.to = alignment; + } + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + + while (block && !after_selection_end) { + WebKitDOMNode *next_block; + + next_block = webkit_dom_node_get_next_sibling (block); + + after_selection_end = webkit_dom_node_contains ( + block, WEBKIT_DOM_NODE (selection_end_marker)); + + if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-indented")) { + gint ii, length; + WebKitDOMNodeList *list = NULL; + + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (block), + ".-x-evo-indented > *:not(.-x-evo-indented):not(li)", + NULL); + length = webkit_dom_node_list_get_length (list); + + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *item = webkit_dom_node_list_item (list, ii); + + set_block_alignment (WEBKIT_DOM_ELEMENT (item), class); + + after_selection_end = webkit_dom_node_contains ( + item, WEBKIT_DOM_NODE (selection_end_marker)); + g_object_unref (item); + if (after_selection_end) + break; + } + + g_clear_object (&list); + } else { + set_block_alignment (WEBKIT_DOM_ELEMENT (block), class); + } + + block = next_block; + } + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_selection_restore (editor_page); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); +} + +/* + * e_html_editor_selection_replace: + * @selection: an #EEditorSelection + * @replacement: a string to replace current selection with + * + * Replaces currently selected text with @replacement. + */ +void +e_editor_dom_selection_replace (EEditorPage *editor_page, + const gchar *replacement) +{ + EEditorHistoryEvent *ev = NULL; + EEditorUndoRedoManager *manager; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) { + WebKitDOMRange *range = NULL; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_REPLACE; + + e_editor_dom_selection_get_coordinates (editor_page, + &ev->before.start.x, + &ev->before.start.y, + &ev->before.end.x, + &ev->before.end.y); + + range = e_editor_dom_get_current_range (editor_page); + + ev->data.string.from = webkit_dom_range_get_text (range); + ev->data.string.to = g_strdup (replacement); + + g_clear_object (&range); + } + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, replacement); + + if (ev) { + e_editor_dom_selection_get_coordinates (editor_page, + &ev->after.start.x, + &ev->after.start.y, + &ev->after.end.x, + &ev->after.end.y); + + e_editor_undo_redo_manager_insert_history_event (manager, ev); + } + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + + e_editor_page_emit_content_changed (editor_page); +} + +/* + * e_html_editor_selection_replace_caret_word: + * @selection: an #EEditorSelection + * @replacement: a string to replace current caret word with + * + * Replaces current word under cursor with @replacement. + */ +void +e_editor_dom_replace_caret_word (EEditorPage *editor_page, + const gchar *replacement) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMDocumentFragment *fragment; + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + e_editor_page_emit_content_changed (editor_page); + range = e_editor_dom_get_current_range (editor_page); + webkit_dom_range_expand (range, "word", NULL); + webkit_dom_dom_selection_add_range (dom_selection, range); + + fragment = webkit_dom_range_extract_contents (range, NULL); + + /* Get the text node to replace and leave other formatting nodes + * untouched (font color, boldness, ...). */ + webkit_dom_node_normalize (WEBKIT_DOM_NODE (fragment)); + node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + if (!WEBKIT_DOM_IS_TEXT (node)) { + while (node && WEBKIT_DOM_IS_ELEMENT (node)) + node = webkit_dom_node_get_first_child (node); + } + + if (node && WEBKIT_DOM_IS_TEXT (node)) { + WebKitDOMText *text; + + /* Replace the word */ + text = webkit_dom_document_create_text_node (document, replacement); + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (text), + node, + NULL); + + /* Insert the word on current location. */ + webkit_dom_range_insert_node (range, WEBKIT_DOM_NODE (fragment), NULL); + + webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); + } + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + + g_clear_object (&range); + g_clear_object (&dom_selection); +} + +/* + * e_html_editor_selection_get_caret_word: + * @selection: an #EEditorSelection + * + * Returns word under cursor. + * + * Returns: A newly allocated string with current caret word or @NULL when there + * is no text under cursor or when selection is active. [transfer-full]. + */ +gchar * +e_editor_dom_get_caret_word (EEditorPage *editor_page) +{ + gchar *word; + WebKitDOMRange *range = NULL, *range_clone = NULL; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + range = e_editor_dom_get_current_range (editor_page); + + /* Don't operate on the visible selection */ + range_clone = webkit_dom_range_clone_range (range, NULL); + webkit_dom_range_expand (range_clone, "word", NULL); + word = webkit_dom_range_to_string (range_clone, NULL); + + g_clear_object (&range); + g_clear_object (&range_clone); + + return word; +} + +/* + * e_html_editor_selection_get_list_alignment_from_node: + * @node: #an WebKitDOMNode + * + * Returns alignment of given list. + * + * Returns: #EContentEditorAlignment + */ +EContentEditorAlignment +e_editor_dom_get_list_alignment_from_node (WebKitDOMNode *node) +{ + if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-center")) + return E_CONTENT_EDITOR_ALIGNMENT_CENTER; + if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-right")) + return E_CONTENT_EDITOR_ALIGNMENT_RIGHT; + else + return E_CONTENT_EDITOR_ALIGNMENT_LEFT; +} + +WebKitDOMElement * +e_editor_dom_prepare_paragraph (EEditorPage *editor_page, + gboolean with_selection) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element, *paragraph; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + document = e_editor_page_get_document (editor_page); + paragraph = e_editor_dom_get_paragraph_element (editor_page, -1, 0); + + if (with_selection) + dom_add_selection_markers_into_element_start ( + document, paragraph, NULL, NULL); + + element = webkit_dom_document_create_element (document, "BR", NULL); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (paragraph), WEBKIT_DOM_NODE (element), NULL); + + return paragraph; +} + +void +e_editor_dom_selection_set_on_point (EEditorPage *editor_page, + guint x, + guint y) +{ + WebKitDOMDocument *document; + WebKitDOMRange *range = NULL; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + + range = webkit_dom_document_caret_range_from_point (document, x, y); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + + g_clear_object (&range); + g_clear_object (&dom_selection); + g_clear_object (&dom_window); +} + +void +e_editor_dom_selection_get_coordinates (EEditorPage *editor_page, + guint *start_x, + guint *start_y, + guint *end_x, + guint *end_y) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element, *parent; + gboolean created_selection_markers = FALSE; + guint local_x = 0, local_y = 0; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + g_return_if_fail (start_x != NULL); + g_return_if_fail (start_y != NULL); + g_return_if_fail (end_x != NULL); + g_return_if_fail (end_y != NULL); + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (!element) { + created_selection_markers = TRUE; + e_editor_dom_selection_save (editor_page); + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (!element) + return; + } + + parent = element; + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + local_x += (guint) webkit_dom_element_get_offset_left (parent); + local_y += (guint) webkit_dom_element_get_offset_top (parent); + parent = webkit_dom_element_get_offset_parent (parent); + } + + if (start_x) + *start_x = local_x; + if (start_y) + *start_y = local_y; + + if (e_editor_dom_selection_is_collapsed (editor_page)) { + *end_x = local_x; + *end_y = local_y; + + if (created_selection_markers) + e_editor_dom_selection_restore (editor_page); + + goto workaroud; + } + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + local_x = 0; + local_y = 0; + + parent = element; + while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + local_x += (guint) webkit_dom_element_get_offset_left (parent); + local_y += (guint) webkit_dom_element_get_offset_top (parent); + parent = webkit_dom_element_get_offset_parent (parent); + } + + if (end_x) + *end_x = local_x; + if (end_y) + *end_y = local_y; + + if (created_selection_markers) + e_editor_dom_selection_restore (editor_page); + + workaroud: + /* Workaround for bug 749712 on the Evolution side. The cause of the bug + * is that WebKit is having problems determining the right line height + * for some fonts and font sizes (the right and wrong value differ by 1). + * To fix this we will add an extra one to the final top offset. This is + * safe to do even for fonts and font sizes that don't behave badly as we + * will still get the right element as we use fonts bigger than 1 pixel. */ + *start_y += 1; + *end_y += 1; +} diff --git a/modules/webkit-editor/web-extension/e-editor-dom-functions.h b/modules/webkit-editor/web-extension/e-editor-dom-functions.h new file mode 100644 index 0000000..747dd11 --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-dom-functions.h @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef E_EDITOR_DOM_FUNCTIONS_H +#define E_EDITOR_DOM_FUNCTIONS_H + +#include <webkitdom/webkitdom.h> + +#define E_UTIL_INCLUDE_WITHOUT_WEBKIT +#include <e-util/e-util.h> +#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT + +#include "e-editor-page.h" + +#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b" +#define UNICODE_NBSP "\xc2\xa0" + +/* stephenhay from https://mathiasbynens.be/demo/url-regex */ +#define URL_PROTOCOLS "news|telnet|nntp|file|https?|s?ftp|webcal|localhost|ssh" +#define URL_PATTERN_BASE "(?=((?:(?:(?:" URL_PROTOCOLS ")\\:\\/\\/)|(?:www\\.|ftp\\.))[^\\s\\/\\$\\.\\?#].[^\\s]*)" +#define URL_PATTERN_NO_NBSP ")((?:(?! ).)*)" +#define URL_PATTERN URL_PATTERN_BASE URL_PATTERN_NO_NBSP +#define URL_PATTERN_SPACE URL_PATTERN_BASE "\\s$" URL_PATTERN_NO_NBSP +/* Taken from camel-url-scanner.c */ +#define URL_INVALID_TRAILING_CHARS ",.:;?!-|}])\"" + +/* http://www.w3.org/TR/html5/forms.html#valid-e-mail-address */ +#define E_MAIL_PATTERN \ + "[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}"\ + "[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*" + +#define E_MAIL_PATTERN_SPACE E_MAIL_PATTERN "\\s" + +#define QUOTE_SYMBOL ">" + +#define SPACES_PER_INDENTATION 3 +#define SPACES_PER_LIST_LEVEL 3 +#define SPACES_ORDERED_LIST_FIRST_LEVEL 6 +#define TAB_LENGTH 8 +#define MINIMAL_PARAGRAPH_WIDTH 5 + +G_BEGIN_DECLS + +/* ******************** Tests ******************** */ + +gboolean e_editor_dom_test_html_equal (WebKitDOMDocument *document, + const gchar *html1, + const gchar *html2); + +/* ******************** Actions ******************** */ + +void e_editor_dom_delete_cell_contents + (EEditorPage *editor_page); +void e_editor_dom_delete_column (EEditorPage *editor_page); +void e_editor_dom_delete_row (EEditorPage *editor_page); +void e_editor_dom_delete_table (EEditorPage *editor_page); +void e_editor_dom_insert_column_after + (EEditorPage *editor_page); +void e_editor_dom_insert_column_before + (EEditorPage *editor_page); +void e_editor_dom_insert_row_above (EEditorPage *editor_page); +void e_editor_dom_insert_row_below (EEditorPage *editor_page); +void e_editor_dom_save_history_for_cut + (EEditorPage *editor_page); + +/* ******************** View ******************** */ + +gboolean e_editor_dom_exec_command (EEditorPage *editor_page, + EContentEditorCommand command, + const gchar *value); +void e_editor_dom_force_spell_check_for_current_paragraph + (EEditorPage *editor_page); +void e_editor_dom_force_spell_check_in_viewport + (EEditorPage *editor_page); +void e_editor_dom_force_spell_check (EEditorPage *editor_page); +void e_editor_dom_turn_spell_check_off + (EEditorPage *editor_page); +void e_editor_dom_embed_style_sheet (EEditorPage *editor_page, + const gchar *style_sheet_content); +void e_editor_dom_remove_embedded_style_sheet + (EEditorPage *editor_page); +void e_editor_dom_register_input_event_listener_on_body + (EEditorPage *editor_page); +void e_editor_dom_remove_input_event_listener_from_body + (EEditorPage *editor_page); +void e_editor_dom_quote_and_insert_text_into_selection + (EEditorPage *editor_page, + const gchar *text, + gboolean is_html); +void e_editor_dom_check_magic_links (EEditorPage *editor_page, + gboolean include_space_by_user); +void e_editor_dom_insert_smiley (EEditorPage *editor_page, + EEmoticon *emoticon); +void e_editor_dom_insert_smiley_by_name + (EEditorPage *editor_page, + const gchar *name); +void e_editor_dom_check_magic_smileys + (EEditorPage *editor_page); +void e_editor_dom_set_monospace_font_family_on_body + (WebKitDOMElement *body, + gboolean html_mode); +void e_editor_dom_convert_content (EEditorPage *editor_page, + const gchar *preferred_text); +void e_editor_dom_convert_and_insert_html_into_selection + (EEditorPage *editor_page, + const gchar *html, + gboolean is_html); +gboolean e_editor_dom_node_is_citation_node + (WebKitDOMNode *node); +void e_editor_dom_quote_plain_text_element_after_wrapping + (EEditorPage *editor_page, + WebKitDOMElement *element, + gint quote_level); +WebKitDOMNode * e_editor_dom_get_parent_block_node_from_child + (WebKitDOMNode *node); +WebKitDOMElement * + e_editor_dom_insert_new_line_into_citation + (EEditorPage *editor_page, + const gchar *html_to_insert); +WebKitDOMElement * + e_editor_dom_quote_plain_text_element + (EEditorPage *editor_page, + WebKitDOMElement *element); +void e_editor_dom_convert_when_changing_composer_mode + (EEditorPage *editor_page); +void e_editor_dom_process_content_after_load + (EEditorPage *editor_page); +GVariant * e_editor_dom_get_inline_images_data + (EEditorPage *editor_page, + const gchar *uid_domain); +void e_editor_dom_insert_html (EEditorPage *editor_page, + const gchar *html_text); +void e_editor_dom_convert_element_from_html_to_plain_text + (EEditorPage *editor_page, + WebKitDOMElement *element); +gchar * e_editor_dom_process_content_for_draft + (EEditorPage *editor_page, + gboolean only_inner_body); +gchar * e_editor_dom_process_content_to_plain_text_for_exporting + (EEditorPage *editor_page); +void e_editor_dom_restore_images (EEditorPage *editor_page, + GVariant *inline_images_to_restore); +gchar * e_editor_dom_process_content_to_html_for_exporting + (EEditorPage *editor_page); +gboolean e_editor_dom_check_if_conversion_needed + (EEditorPage *editor_page); +void e_editor_dom_process_content_after_mode_change + (EEditorPage *editor_page); +guint e_editor_dom_get_caret_offset (EEditorPage *editor_page); +guint e_editor_dom_get_caret_position (EEditorPage *editor_page); +void e_editor_dom_drag_and_drop_end (EEditorPage *editor_page); +void e_editor_dom_set_link_color (EEditorPage *editor_page, + const gchar *color); +void e_editor_dom_set_visited_link_color + (EEditorPage *editor_page, + const gchar *color); +gboolean e_editor_dom_move_quoted_block_level_up + (EEditorPage *editor_page); +gboolean e_editor_dom_delete_last_character_on_line_in_quoted_block + (EEditorPage *editor_page, + glong key_code, + gboolean control_key); +gboolean e_editor_dom_fix_structure_after_delete_before_quoted_content + (EEditorPage *editor_page, + glong key_code, + gboolean control_key, + gboolean delete_key); +void e_editor_dom_disable_quote_marks_select + (EEditorPage *editor_page); +void e_editor_dom_remove_node_and_parents_if_empty + (WebKitDOMNode *node); +gboolean e_editor_dom_return_pressed_in_empty_list_item + (EEditorPage *editor_page); +void e_editor_dom_merge_siblings_if_necessary + (EEditorPage *editor_page, + WebKitDOMDocumentFragment *deleted_content); +void e_editor_dom_body_key_up_event_process_return_key + (EEditorPage *editor_page); +gboolean e_editor_dom_key_press_event_process_backspace_key + (EEditorPage *editor_page); +gboolean e_editor_dom_key_press_event_process_delete_or_backspace_key + (EEditorPage *editor_page, + glong key_code, + gboolean control_key, + gboolean delete); +void e_editor_dom_body_input_event_process + (EEditorPage *editor_page, + WebKitDOMEvent *event); +void e_editor_dom_body_key_up_event_process_backspace_or_delete + (EEditorPage *editor_page, + gboolean delete); +gboolean e_editor_dom_key_press_event_process_return_key + (EEditorPage *editor_page); +WebKitDOMElement * + e_editor_dom_wrap_and_quote_element + (EEditorPage *editor_page, + WebKitDOMElement *element); +gint e_editor_dom_get_citation_level (WebKitDOMNode *node, + gboolean set_plaintext_quoted); +void e_editor_dom_save_history_for_drop + (EEditorPage *editor_page); +void e_editor_dom_fix_file_uri_images + (EEditorPage *editor_page); + +/* ******************** Selection ******************** */ + +void e_editor_dom_replace_base64_image_src + (EEditorPage *editor_page, + const gchar *selector, + const gchar *base64_content, + const gchar *filename, + const gchar *uri); +WebKitDOMRange * + e_editor_dom_get_current_range (EEditorPage *editor_page); +void e_editor_dom_move_caret_into_element + (EEditorPage *editor_page, + WebKitDOMElement *element, + gboolean to_start); +void e_editor_dom_insert_base64_image + (EEditorPage *editor_page, + const gchar *base64_content, + const gchar *filename, + const gchar *uri); +void e_editor_dom_insert_image (EEditorPage *editor_page, + const gchar *uri); +void e_editor_dom_replace_image_src (EEditorPage *editor_page, + const gchar *selector, + const gchar *uri); +void e_editor_dom_selection_unlink (EEditorPage *editor_page); +void e_editor_dom_create_link (EEditorPage *editor_page, + const gchar *uri); +void e_editor_dom_selection_indent (EEditorPage *editor_page); +void e_editor_dom_selection_unindent (EEditorPage *editor_page); +void e_editor_dom_selection_save (EEditorPage *editor_page); +void e_editor_dom_selection_restore (EEditorPage *editor_page); +gboolean e_editor_dom_selection_is_collapsed + (EEditorPage *editor_page); +void e_editor_dom_scroll_to_caret (EEditorPage *editor_page); +void e_editor_dom_remove_wrapping_from_element + (WebKitDOMElement *element); +void e_editor_dom_remove_quoting_from_element + (WebKitDOMElement *element); +void e_editor_dom_set_paragraph_style + (EEditorPage *editor_page, + WebKitDOMElement *element, + gint width, + gint offset, + const gchar *style_to_add); +WebKitDOMElement * + e_editor_dom_get_paragraph_element + (EEditorPage *editor_page, + gint width, + gint offset); +WebKitDOMElement * + e_editor_dom_put_node_into_paragraph + (EEditorPage *editor_page, + WebKitDOMNode *node, + gboolean with_input); +void e_editor_dom_selection_wrap (EEditorPage *editor_page); +WebKitDOMElement * + e_editor_dom_wrap_paragraph_length + (EEditorPage *editor_page, + WebKitDOMElement *paragraph, + gint length); +WebKitDOMElement * + e_editor_dom_wrap_paragraph (EEditorPage *editor_page, + WebKitDOMElement *paragraph); +void e_editor_dom_wrap_paragraphs_in_document + (EEditorPage *editor_page); +gboolean e_editor_dom_selection_is_underline + (EEditorPage *editor_page); +void e_editor_dom_selection_set_underline + (EEditorPage *editor_page, + gboolean underline); +gboolean e_editor_dom_selection_is_subscript + (EEditorPage *editor_page); +void e_editor_dom_selection_set_subscript + (EEditorPage *editor_page, + gboolean subscript); +gboolean e_editor_dom_selection_is_superscript + (EEditorPage *editor_page); +void e_editor_dom_selection_set_superscript + (EEditorPage *editor_page, + gboolean superscript); +gboolean e_editor_dom_selection_is_strikethrough + (EEditorPage *editor_page); +void e_editor_dom_selection_set_strikethrough + (EEditorPage *editor_page, + gboolean strikethrough); +gboolean e_editor_dom_selection_is_monospace + (EEditorPage *editor_page); +void e_editor_dom_selection_set_monospace + (EEditorPage *editor_page, + gboolean monospaced); +gboolean e_editor_dom_selection_is_bold (EEditorPage *editor_page); +void e_editor_dom_selection_set_bold (EEditorPage *editor_page, + gboolean bold); +gboolean e_editor_dom_selection_is_italic + (EEditorPage *editor_page); +void e_editor_dom_selection_set_italic + (EEditorPage *editor_page, + gboolean italic); +gboolean e_editor_dom_selection_is_indented + (EEditorPage *editor_page); +gboolean e_editor_dom_selection_is_citation + (EEditorPage *editor_page); +guint e_editor_dom_selection_get_font_size + (EEditorPage *editor_page); +void e_editor_dom_selection_set_font_size + (EEditorPage *editor_page, + guint font_size); +gchar * e_editor_dom_selection_get_font_name + (EEditorPage *editor_page); +void e_editor_dom_selection_set_font_name + (EEditorPage *editor_page, + const gchar *font_size); +gchar * e_editor_dom_selection_get_font_color + (EEditorPage *editor_page); +void e_editor_dom_selection_set_font_color + (EEditorPage *editor_page, + const gchar *font_color); +gchar * e_editor_dom_selection_get_background_color + (EEditorPage *editor_page); +void e_editor_dom_selection_set_background_color + (EEditorPage *editor_page, + const gchar *bg_color); +EContentEditorBlockFormat + e_editor_dom_selection_get_block_format + (EEditorPage *editor_page); +void e_editor_dom_selection_set_block_format + (EEditorPage *editor_page, + EContentEditorBlockFormat format); +EContentEditorAlignment + e_editor_dom_selection_get_alignment + (EEditorPage *editor_page); +void e_editor_dom_selection_set_alignment + (EEditorPage *editor_page, + EContentEditorAlignment alignment); +void e_editor_dom_selection_replace (EEditorPage *editor_page, + const gchar *replacement); +void e_editor_dom_replace_caret_word (EEditorPage *editor_page, + const gchar *replacement); +gchar * e_editor_dom_get_caret_word (EEditorPage *editor_page); +EContentEditorAlignment + e_editor_dom_get_list_alignment_from_node + (WebKitDOMNode *node); +WebKitDOMElement * + e_editor_dom_prepare_paragraph (EEditorPage *editor_page, + gboolean with_selection); +void e_editor_dom_selection_set_on_point + (EEditorPage *editor_page, + guint x, + guint y); +void e_editor_dom_selection_get_coordinates + (EEditorPage *editor_page, + guint *start_x, + guint *start_y, + guint *end_x, + guint *end_y); +gboolean e_editor_dom_is_selection_position_node + (WebKitDOMNode *node); + +G_END_DECLS + +#endif /* E_EDITOR_DOM_FUNCTIONS_H */ diff --git a/modules/webkit-editor/web-extension/e-editor-page.c b/modules/webkit-editor/web-extension/e-editor-page.c new file mode 100644 index 0000000..6d362e5 --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-page.c @@ -0,0 +1,940 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <webkit2/webkit-web-extension.h> + +#include "web-extensions/e-dom-utils.h" + +#include "e-editor-dom-functions.h" +#include "e-editor-web-extension.h" +#include "e-editor-undo-redo-manager.h" + +#include "e-editor-page.h" + +struct _EEditorPagePrivate { + WebKitWebPage *web_page; /* not referenced */ + EEditorWebExtension *web_extension; /* not referenced */ + + EEditorUndoRedoManager *undo_redo_manager; + ESpellChecker *spell_checker; + + guint spell_check_on_scroll_event_source_id; + + EContentEditorAlignment alignment; + EContentEditorBlockFormat block_format; + guint32 style_flags; /* bit-OR of EContentEditorStyleFlags */ + gchar *background_color; + gchar *font_color; + gchar *font_name; + gint font_size; + + guint selection_changed_blocked; + gboolean selection_changed; + + gboolean force_image_load; + gboolean html_mode; + gboolean return_key_pressed; + gboolean space_key_pressed; + gboolean smiley_written; + gint word_wrap_length; + + gboolean convert_in_situ; + gboolean body_input_event_removed; + gboolean dont_save_history_in_body_input; + gboolean composition_in_progress; + gboolean pasting_content_from_itself; + gboolean renew_history_after_coordinates; + + GHashTable *inline_images; + + WebKitDOMNode *node_under_mouse_click; + + GSettings *mail_settings; +}; + +G_DEFINE_TYPE (EEditorPage, e_editor_page, G_TYPE_OBJECT) + + +static void +web_page_document_loaded_cb (WebKitWebPage *web_page, + EEditorPage *editor_page) +{ + g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page)); + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->body_input_event_removed = TRUE; + + e_editor_undo_redo_manager_clean_history (editor_page->priv->undo_redo_manager); + e_editor_dom_process_content_after_load (editor_page); +} + +static gboolean +web_page_context_menu_cb (WebKitWebPage *web_page, + WebKitContextMenu *context_menu, + WebKitWebHitTestResult *hit_test_result, + EEditorPage *editor_page) +{ + WebKitDOMNode *node; + EContentEditorNodeFlags flags = 0; + GVariant *variant; + + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + node = webkit_web_hit_test_result_get_node (hit_test_result); + editor_page->priv->node_under_mouse_click = node; + + if (WEBKIT_DOM_IS_HTML_HR_ELEMENT (node)) + flags |= E_CONTENT_EDITOR_NODE_IS_H_RULE; + + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) || + (dom_node_find_parent_element (node, "A") != NULL)) + flags |= E_CONTENT_EDITOR_NODE_IS_ANCHOR; + + if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node) || + (dom_node_find_parent_element (node, "IMG") != NULL)) + flags |= E_CONTENT_EDITOR_NODE_IS_IMAGE; + + if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node) || + (dom_node_find_parent_element (node, "TD") != NULL) || + (dom_node_find_parent_element (node, "TH") != NULL)) + flags |= E_CONTENT_EDITOR_NODE_IS_TABLE_CELL; + + if (flags & E_CONTENT_EDITOR_NODE_IS_TABLE_CELL && + (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (node) || + dom_node_find_parent_element (node, "TABLE") != NULL)) + flags |= E_CONTENT_EDITOR_NODE_IS_TABLE; + + if (flags == 0) + flags |= E_CONTENT_EDITOR_NODE_IS_TEXT; + + variant = g_variant_new_int32 (flags); + webkit_context_menu_set_user_data (context_menu, variant); + + return FALSE; +} + +static void +e_editor_page_setup (EEditorPage *editor_page, + WebKitWebPage *web_page, + struct _EEditorWebExtension *web_extension) +{ + WebKitWebEditor *web_editor; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->web_page = web_page; + editor_page->priv->web_extension = web_extension; + editor_page->priv->undo_redo_manager = e_editor_undo_redo_manager_new (editor_page); + + g_signal_connect_swapped ( + editor_page->priv->undo_redo_manager, "notify::can-undo", + G_CALLBACK (e_editor_page_emit_undo_redo_state_changed), editor_page); + + g_signal_connect_swapped ( + editor_page->priv->undo_redo_manager, "notify::can-redo", + G_CALLBACK (e_editor_page_emit_undo_redo_state_changed), editor_page); + + web_editor = webkit_web_page_get_editor (web_page); + + g_signal_connect_swapped ( + web_editor, "selection-changed", + G_CALLBACK (e_editor_page_emit_selection_changed), editor_page); + + g_signal_connect ( + web_page, "document-loaded", + G_CALLBACK (web_page_document_loaded_cb), editor_page); + + g_signal_connect ( + web_page, "context-menu", + G_CALLBACK (web_page_context_menu_cb), editor_page); +} + +static void +e_editor_page_dispose (GObject *object) +{ + EEditorPage *editor_page = E_EDITOR_PAGE (object); + + if (editor_page->priv->spell_check_on_scroll_event_source_id > 0) { + g_source_remove (editor_page->priv->spell_check_on_scroll_event_source_id); + editor_page->priv->spell_check_on_scroll_event_source_id = 0; + } + + if (editor_page->priv->background_color != NULL) { + g_free (editor_page->priv->background_color); + editor_page->priv->background_color = NULL; + } + + if (editor_page->priv->font_color != NULL) { + g_free (editor_page->priv->font_color); + editor_page->priv->font_color = NULL; + } + + if (editor_page->priv->font_name != NULL) { + g_free (editor_page->priv->font_name); + editor_page->priv->font_name = NULL; + } + + if (editor_page->priv->mail_settings != NULL) { + g_signal_handlers_disconnect_by_data (editor_page->priv->mail_settings, object); + g_object_unref (editor_page->priv->mail_settings); + editor_page->priv->mail_settings = NULL; + } + + g_clear_object (&editor_page->priv->undo_redo_manager); + g_clear_object (&editor_page->priv->spell_checker); + + g_hash_table_remove_all (editor_page->priv->inline_images); + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_editor_page_parent_class)->dispose (object); +} + +static void +e_editor_page_finalize (GObject *object) +{ + EEditorPage *editor_page = E_EDITOR_PAGE (object); + + g_hash_table_destroy (editor_page->priv->inline_images); + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_editor_page_parent_class)->finalize (object); +} + +static void +e_editor_page_class_init (EEditorPageClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EEditorPagePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = e_editor_page_dispose; + object_class->finalize = e_editor_page_finalize; +} + +static void +e_editor_page_init (EEditorPage *editor_page) +{ + editor_page->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor_page, E_TYPE_EDITOR_PAGE, EEditorPagePrivate); + editor_page->priv->style_flags = 0; + editor_page->priv->selection_changed_blocked = 0; + editor_page->priv->background_color = g_strdup (""); + editor_page->priv->font_color = g_strdup (""); + editor_page->priv->font_name = g_strdup (""); + editor_page->priv->font_size = E_CONTENT_EDITOR_FONT_SIZE_NORMAL; + editor_page->priv->alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT; + editor_page->priv->block_format = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH; + editor_page->priv->force_image_load = FALSE; + editor_page->priv->html_mode = TRUE; + editor_page->priv->return_key_pressed = FALSE; + editor_page->priv->space_key_pressed = FALSE; + editor_page->priv->smiley_written = FALSE; + editor_page->priv->convert_in_situ = FALSE; + editor_page->priv->body_input_event_removed = TRUE; + editor_page->priv->dont_save_history_in_body_input = FALSE; + editor_page->priv->pasting_content_from_itself = FALSE; + editor_page->priv->composition_in_progress = FALSE; + editor_page->priv->renew_history_after_coordinates = TRUE; + editor_page->priv->spell_check_on_scroll_event_source_id = 0; + editor_page->priv->mail_settings = e_util_ref_settings ("org.gnome.evolution.mail"); + editor_page->priv->word_wrap_length = g_settings_get_int (editor_page->priv->mail_settings, "composer-word-wrap-length"); + editor_page->priv->inline_images = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + editor_page->priv->spell_checker = e_spell_checker_new (); +} + +EEditorPage * +e_editor_page_new (WebKitWebPage *web_page, + struct _EEditorWebExtension *web_extension) +{ + EEditorPage *editor_page; + + g_return_val_if_fail (WEBKIT_IS_WEB_PAGE (web_page), NULL); + g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (web_extension), NULL); + + editor_page = g_object_new (E_TYPE_EDITOR_PAGE, NULL); + e_editor_page_setup (editor_page, web_page, web_extension); + + return editor_page; +} + +WebKitWebPage * +e_editor_page_get_web_page (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return editor_page->priv->web_page; +} + +struct _EEditorWebExtension * +e_editor_page_get_web_extension (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return editor_page->priv->web_extension; +} + +guint64 +e_editor_page_get_page_id (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + if (!editor_page->priv->web_page) + return 0; + + return webkit_web_page_get_id (editor_page->priv->web_page); +} + +WebKitDOMDocument * +e_editor_page_get_document (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + if (!editor_page->priv->web_page) + return NULL; + + return webkit_web_page_get_dom_document (editor_page->priv->web_page); +} + +struct _EEditorUndoRedoManager * +e_editor_page_get_undo_redo_manager (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return editor_page->priv->undo_redo_manager; +} + +void +e_editor_page_block_selection_changed (EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->selection_changed_blocked++; +} + +void +e_editor_page_unblock_selection_changed (EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + g_return_if_fail (editor_page->priv->selection_changed_blocked > 0); + + editor_page->priv->selection_changed_blocked--; + + if (!editor_page->priv->selection_changed_blocked && + editor_page->priv->selection_changed) { + editor_page->priv->selection_changed = FALSE; + e_editor_page_emit_selection_changed (editor_page); + } +} + +gboolean +e_editor_page_get_html_mode (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->html_mode; +} + +void +e_editor_page_set_html_mode (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->html_mode = value; +} + +gboolean +e_editor_page_get_force_image_load (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->force_image_load; +} + +void +e_editor_page_set_force_image_load (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->force_image_load = value; +} + +gint +e_editor_page_get_word_wrap_length (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + return editor_page->priv->word_wrap_length; +} + +static gboolean +e_editor_page_check_style_flag (EEditorPage *editor_page, + EContentEditorStyleFlags flag) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return (editor_page->priv->style_flags & flag) != 0; +} + +static gboolean +e_editor_page_set_style_flag (EEditorPage *editor_page, + EContentEditorStyleFlags flag, + gboolean value) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + if ((((editor_page->priv->style_flags & flag) != 0) ? 1 : 0) == (value ? 1 : 0)) + return FALSE; + + editor_page->priv->style_flags = (editor_page->priv->style_flags & ~flag) | (value ? flag : 0); + + return TRUE; +} + +gboolean +e_editor_page_get_bold (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_BOLD); +} + +void +e_editor_page_set_bold (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_BOLD, value)) + e_editor_dom_selection_set_bold (editor_page, value); +} + +gboolean +e_editor_page_get_italic (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_ITALIC); +} + +void +e_editor_page_set_italic (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_ITALIC, value)) + e_editor_dom_selection_set_italic (editor_page, value); +} + +gboolean +e_editor_page_get_underline (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_UNDERLINE); +} + +void +e_editor_page_set_underline (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_UNDERLINE, value)) + e_editor_dom_selection_set_underline (editor_page, value); +} + +gboolean +e_editor_page_get_monospace (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_MONOSPACE); +} + +void +e_editor_page_set_monospace (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_MONOSPACE, value)) + e_editor_dom_selection_set_monospace (editor_page, value); +} + +gboolean +e_editor_page_get_strikethrough (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH); +} + +void +e_editor_page_set_strikethrough (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH, value)) + e_editor_dom_selection_set_strikethrough (editor_page, value); +} + +guint +e_editor_page_get_font_size (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + return editor_page->priv->font_size; +} + +void +e_editor_page_set_font_size (EEditorPage *editor_page, + guint value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (editor_page->priv->font_size == value) + return; + + editor_page->priv->font_size = value; +} + +const gchar * +e_editor_page_get_font_color (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return editor_page->priv->font_color; +} + +EContentEditorAlignment +e_editor_page_get_alignment (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_CONTENT_EDITOR_ALIGNMENT_LEFT); + + return editor_page->priv->alignment; +} + +void +e_editor_page_set_alignment (EEditorPage *editor_page, + EContentEditorAlignment value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->alignment = value; +} + +gboolean +e_editor_page_get_return_key_pressed (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->return_key_pressed; +} + +void +e_editor_page_set_return_key_pressed (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->return_key_pressed = value; +} + +gboolean +e_editor_page_get_space_key_pressed (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->space_key_pressed; +} + +void +e_editor_page_set_space_key_pressed (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->space_key_pressed = value; +} + +gboolean +e_editor_page_get_magic_links_enabled (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return g_settings_get_boolean (editor_page->priv->mail_settings, "composer-magic-links"); +} + +gboolean +e_editor_page_get_magic_smileys_enabled (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return g_settings_get_boolean (editor_page->priv->mail_settings, "composer-magic-smileys"); +} + +gboolean +e_editor_page_get_unicode_smileys_enabled (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return g_settings_get_boolean (editor_page->priv->mail_settings, "composer-unicode-smileys"); +} + +EImageLoadingPolicy +e_editor_page_get_image_loading_policy (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_IMAGE_LOADING_POLICY_NEVER); + + return g_settings_get_enum (editor_page->priv->mail_settings, "image-loading-policy"); +} + +gboolean +e_editor_page_get_inline_spelling_enabled (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return g_settings_get_boolean (editor_page->priv->mail_settings, "composer-inline-spelling"); +} + +gboolean +e_editor_page_check_word_spelling (EEditorPage *editor_page, + const gchar *word, + const gchar * const *languages) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), TRUE); + + if (!word || !languages || !*languages) + return TRUE; + + e_spell_checker_set_active_languages (editor_page->priv->spell_checker, languages); + + return e_spell_checker_check_word (editor_page->priv->spell_checker, word, -1); +} + +gboolean +e_editor_page_get_body_input_event_removed (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->body_input_event_removed; +} + +void +e_editor_page_set_body_input_event_removed (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->body_input_event_removed = value; +} + +gboolean +e_editor_page_get_convert_in_situ (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->convert_in_situ; +} + +void +e_editor_page_set_convert_in_situ (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->convert_in_situ = value; +} + +GHashTable * +e_editor_page_get_inline_images (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return editor_page->priv->inline_images; +} + +void +e_editor_page_add_new_inline_image_into_list (EEditorPage *editor_page, + const gchar *cid_src, + const gchar *src) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_hash_table_insert (editor_page->priv->inline_images, g_strdup (cid_src), g_strdup (src)); +} + +gboolean +e_editor_page_get_is_smiley_written (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->smiley_written; +} + +void +e_editor_page_set_is_smiley_written (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->smiley_written = value; +} + +gboolean +e_editor_page_get_dont_save_history_in_body_input (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->dont_save_history_in_body_input; +} + +void +e_editor_page_set_dont_save_history_in_body_input (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->dont_save_history_in_body_input = value; +} + +gboolean +e_editor_page_is_pasting_content_from_itself (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->pasting_content_from_itself; +} + +void +e_editor_page_set_pasting_content_from_itself (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->pasting_content_from_itself = value; +} + +gboolean +e_editor_page_get_renew_history_after_coordinates (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->renew_history_after_coordinates; +} + +void +e_editor_page_set_renew_history_after_coordinates (EEditorPage *editor_page, + gboolean renew_history_after_coordinates) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->renew_history_after_coordinates = renew_history_after_coordinates; +} + +gboolean +e_editor_page_is_composition_in_progress (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE); + + return editor_page->priv->composition_in_progress; +} + +void +e_editor_page_set_composition_in_progress (EEditorPage *editor_page, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->composition_in_progress = value; +} + +guint +e_editor_page_get_spell_check_on_scroll_event_source_id (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0); + + return editor_page->priv->spell_check_on_scroll_event_source_id; +} + +void +e_editor_page_set_spell_check_on_scroll_event_source_id (EEditorPage *editor_page, + guint value) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + editor_page->priv->spell_check_on_scroll_event_source_id = value; +} + +WebKitDOMNode * +e_editor_page_get_node_under_mouse_click (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return editor_page->priv->node_under_mouse_click; +} + +void +e_editor_page_emit_selection_changed (EEditorPage *editor_page) +{ + WebKitDOMDocument *document; + WebKitDOMRange *range = NULL; + GDBusConnection *connection; + GError *error = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!editor_page->priv->web_extension || + editor_page->priv->selection_changed_blocked) { + editor_page->priv->selection_changed = TRUE; + return; + } + + document = e_editor_page_get_document (editor_page); + if (!document) + return; + + connection = e_editor_web_extension_get_connection (editor_page->priv->web_extension); + if (!connection) + return; + + range = e_editor_dom_get_current_range (editor_page); + if (!range) + return; + + g_clear_object (&range); + + editor_page->priv->alignment = e_editor_dom_selection_get_alignment (editor_page); + editor_page->priv->block_format = e_editor_dom_selection_get_block_format (editor_page); + + if (editor_page->priv->html_mode) { + guint32 style_flags = E_CONTENT_EDITOR_STYLE_NONE; + + #define set_flag_if(tst, flg) G_STMT_START { \ + if (tst (editor_page)) \ + style_flags |= flg; \ + } G_STMT_END + + set_flag_if (e_editor_dom_selection_is_bold, E_CONTENT_EDITOR_STYLE_IS_BOLD); + set_flag_if (e_editor_dom_selection_is_italic, E_CONTENT_EDITOR_STYLE_IS_ITALIC); + set_flag_if (e_editor_dom_selection_is_underline, E_CONTENT_EDITOR_STYLE_IS_UNDERLINE); + set_flag_if (e_editor_dom_selection_is_strikethrough, E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH); + set_flag_if (e_editor_dom_selection_is_monospace, E_CONTENT_EDITOR_STYLE_IS_MONOSPACE); + set_flag_if (e_editor_dom_selection_is_subscript, E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT); + set_flag_if (e_editor_dom_selection_is_superscript, E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT); + + #undef set_flag_if + + editor_page->priv->style_flags = style_flags; + editor_page->priv->font_size = e_editor_dom_selection_get_font_size (editor_page); + g_free (editor_page->priv->font_color); + editor_page->priv->font_color = e_editor_dom_selection_get_font_color (editor_page); + } + + g_dbus_connection_emit_signal ( + connection, + NULL, + E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, + E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE, + "SelectionChanged", + g_variant_new ("(tiibiis)", + e_editor_page_get_page_id (editor_page), + (gint32) editor_page->priv->alignment, + (gint32) editor_page->priv->block_format, + e_editor_dom_selection_is_indented (editor_page), + editor_page->priv->style_flags, + (gint32) editor_page->priv->font_size, + editor_page->priv->font_color ? editor_page->priv->font_color : ""), + &error); + + if (error) { + g_warning ("%s: Failed to emit signal: %s", G_STRFUNC, error->message); + g_error_free (error); + } +} + +void +e_editor_page_emit_content_changed (EEditorPage *editor_page) +{ + GDBusConnection *connection; + GError *error = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!editor_page->priv->web_extension) + return; + + connection = e_editor_web_extension_get_connection (editor_page->priv->web_extension); + if (!connection) + return; + + g_dbus_connection_emit_signal ( + connection, + NULL, + E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, + E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE, + "ContentChanged", + g_variant_new ("(t)", e_editor_page_get_page_id (editor_page)), + &error); + + if (error) { + g_warning ("%s: Failed to emit signal: %s", G_STRFUNC, error->message); + g_error_free (error); + } +} + +void +e_editor_page_emit_undo_redo_state_changed (EEditorPage *editor_page) +{ + GDBusConnection *connection; + GError *error = NULL; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + if (!editor_page->priv->web_extension) + return; + + connection = e_editor_web_extension_get_connection (editor_page->priv->web_extension); + if (!connection) + return; + + g_dbus_connection_emit_signal ( + connection, + NULL, + E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, + E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE, + "UndoRedoStateChanged", + g_variant_new ("(tbb)", + e_editor_page_get_page_id (editor_page), + e_editor_undo_redo_manager_can_undo (editor_page->priv->undo_redo_manager), + e_editor_undo_redo_manager_can_redo (editor_page->priv->undo_redo_manager)), + &error); + + if (error) { + g_warning ("%s: Failed to emit signal: %s", G_STRFUNC, error->message); + g_error_free (error); + } +} diff --git a/modules/webkit-editor/web-extension/e-editor-page.h b/modules/webkit-editor/web-extension/e-editor-page.h new file mode 100644 index 0000000..160b9ae --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-page.h @@ -0,0 +1,197 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This library 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. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef E_EDITOR_PAGE_H +#define E_EDITOR_PAGE_H + +#include <glib-object.h> +#include <webkit2/webkit-web-extension.h> + +#define E_UTIL_INCLUDE_WITHOUT_WEBKIT +#include <e-util/e-util.h> +#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT + +/* Standard GObject macros */ +#define E_TYPE_EDITOR_PAGE \ + (e_editor_page_get_type ()) +#define E_EDITOR_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_EDITOR_PAGE, EEditorPage)) +#define E_EDITOR_PAGE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_EDITOR_PAGE, EEditorPageClass)) +#define E_IS_EDITOR_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_EDITOR_PAGE)) +#define E_IS_EDITOR_PAGE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_EDITOR_PAGE)) +#define E_EDITOR_PAGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_EDITOR_PAGE, EEditorPageClass)) + +G_BEGIN_DECLS + +struct _EEditorWebExtension; +struct _EEditorUndoRedoManager; + +typedef struct _EEditorPage EEditorPage; +typedef struct _EEditorPageClass EEditorPageClass; +typedef struct _EEditorPagePrivate EEditorPagePrivate; + +struct _EEditorPage { + GObject parent; + EEditorPagePrivate *priv; +}; + +struct _EEditorPageClass { + GObjectClass parent_class; +}; + +GType e_editor_page_get_type (void) G_GNUC_CONST; +EEditorPage * e_editor_page_new (WebKitWebPage *web_page, + struct _EEditorWebExtension *web_extension); +WebKitWebPage * e_editor_page_get_web_page (EEditorPage *editor_page); +struct _EEditorWebExtension * + e_editor_page_get_web_extension (EEditorPage *editor_page); +guint64 e_editor_page_get_page_id (EEditorPage *editor_page); +WebKitDOMDocument * + e_editor_page_get_document (EEditorPage *editor_page); +struct _EEditorUndoRedoManager * + e_editor_page_get_undo_redo_manager + (EEditorPage *editor_page); + +void e_editor_page_block_selection_changed + (EEditorPage *editor_page); +void e_editor_page_unblock_selection_changed + (EEditorPage *editor_page); +gboolean e_editor_page_get_html_mode (EEditorPage *editor_page); +void e_editor_page_set_html_mode (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_force_image_load + (EEditorPage *editor_page); +void e_editor_page_set_force_image_load + (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_bold (EEditorPage *editor_page); +void e_editor_page_set_bold (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_italic (EEditorPage *editor_page); +void e_editor_page_set_italic (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_underline (EEditorPage *editor_page); +void e_editor_page_set_underline (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_monospace (EEditorPage *editor_page); +void e_editor_page_set_monospace (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_strikethrough (EEditorPage *editor_page); +void e_editor_page_set_strikethrough (EEditorPage *editor_page, + gboolean value); +guint e_editor_page_get_font_size (EEditorPage *editor_page); +void e_editor_page_set_font_size (EEditorPage *editor_page, + guint value); +const gchar * e_editor_page_get_font_color (EEditorPage *editor_page); +EContentEditorAlignment + e_editor_page_get_alignment (EEditorPage *editor_page); +void e_editor_page_set_alignment (EEditorPage *editor_page, + EContentEditorAlignment value); +gint e_editor_page_get_word_wrap_length + (EEditorPage *editor_page); +gboolean e_editor_page_get_return_key_pressed + (EEditorPage *editor_page); +void e_editor_page_set_return_key_pressed + (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_space_key_pressed + (EEditorPage *editor_page); +void e_editor_page_set_space_key_pressed + (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_magic_links_enabled + (EEditorPage *editor_page); +gboolean e_editor_page_get_magic_smileys_enabled + (EEditorPage *editor_page); +gboolean e_editor_page_get_unicode_smileys_enabled + (EEditorPage *editor_page); +EImageLoadingPolicy + e_editor_page_get_image_loading_policy + (EEditorPage *editor_page); +gboolean e_editor_page_get_inline_spelling_enabled + (EEditorPage *editor_page); +gboolean e_editor_page_check_word_spelling + (EEditorPage *editor_page, + const gchar *word, + const gchar * const *languages); +gboolean e_editor_page_get_body_input_event_removed + (EEditorPage *editor_page); +void e_editor_page_set_body_input_event_removed + (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_convert_in_situ + (EEditorPage *editor_page); +void e_editor_page_set_convert_in_situ + (EEditorPage *editor_page, + gboolean value); +GHashTable * e_editor_page_get_inline_images + (EEditorPage *editor_page); +void e_editor_page_add_new_inline_image_into_list + (EEditorPage *editor_page, + const gchar *cid_src, + const gchar *src); +gboolean e_editor_page_get_is_smiley_written + (EEditorPage *editor_page); +void e_editor_page_set_is_smiley_written + (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_dont_save_history_in_body_input + (EEditorPage *editor_page); +void e_editor_page_set_dont_save_history_in_body_input + (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_is_pasting_content_from_itself + (EEditorPage *editor_page); +void e_editor_page_set_pasting_content_from_itself + (EEditorPage *editor_page, + gboolean value); +gboolean e_editor_page_get_renew_history_after_coordinates + (EEditorPage *editor_page); +void e_editor_page_set_renew_history_after_coordinates + (EEditorPage *editor_page, + gboolean renew_history_after_coordinates); +gboolean e_editor_page_is_composition_in_progress + (EEditorPage *editor_page); +void e_editor_page_set_composition_in_progress + (EEditorPage *editor_page, + gboolean value); +guint e_editor_page_get_spell_check_on_scroll_event_source_id + (EEditorPage *editor_page); +void e_editor_page_set_spell_check_on_scroll_event_source_id + (EEditorPage *editor_page, + guint value); +WebKitDOMNode * e_editor_page_get_node_under_mouse_click + (EEditorPage *editor_page); + +void e_editor_page_emit_selection_changed + (EEditorPage *editor_page); +void e_editor_page_emit_content_changed + (EEditorPage *editor_page); +void e_editor_page_emit_undo_redo_state_changed + (EEditorPage *editor_page); +G_END_DECLS + +#endif /* E_EDITOR_PAGE_H */ diff --git a/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c b/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c new file mode 100644 index 0000000..2f36941 --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c @@ -0,0 +1,2831 @@ +/* + * e-editor-undo-redo-manager.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define WEBKIT_DOM_USE_UNSTABLE_API +#include <webkitdom/WebKitDOMDocumentFragmentUnstable.h> +#include <webkitdom/WebKitDOMRangeUnstable.h> +#include <webkitdom/WebKitDOMDOMSelection.h> +#include <webkitdom/WebKitDOMDOMWindowUnstable.h> +#include <webkitdom/WebKitDOMHTMLElementUnstable.h> +#include <webkitdom/WebKitDOMDocumentUnstable.h> +#undef WEBKIT_DOM_USE_UNSTABLE_API + +#include "web-extensions/e-dom-utils.h" + +#include "e-editor-page.h" +#include "e-editor-dom-functions.h" +#include "e-editor-undo-redo-manager.h" + +#define E_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_EDITOR_UNDO_REDO_MANAGER, EEditorUndoRedoManagerPrivate)) + +struct _EEditorUndoRedoManagerPrivate { + GWeakRef editor_page; + + gboolean operation_in_progress; + + GList *history; + guint history_size; +}; + +enum { + PROP_0, + PROP_CAN_REDO, + PROP_CAN_UNDO, + PROP_EDITOR_PAGE +}; + +#define HISTORY_SIZE_LIMIT 30 + +#define d(x) + +G_DEFINE_TYPE (EEditorUndoRedoManager, e_editor_undo_redo_manager, G_TYPE_OBJECT) + +EEditorUndoRedoManager * +e_editor_undo_redo_manager_new (EEditorPage *editor_page) +{ + g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL); + + return g_object_new (E_TYPE_EDITOR_UNDO_REDO_MANAGER, + "editor-page", editor_page, + NULL); +} + +static EEditorPage * +editor_undo_redo_manager_ref_editor_page (EEditorUndoRedoManager *manager) +{ + g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), NULL); + + return g_weak_ref_get (&manager->priv->editor_page); +} + +static WebKitDOMRange * +get_range_for_point (WebKitDOMDocument *document, + EEditorSelectionPoint point) +{ + glong scroll_left, scroll_top; + WebKitDOMHTMLElement *body; + WebKitDOMRange *range = NULL; + + body = webkit_dom_document_get_body (document); + scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body)); + scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body)); + + range = webkit_dom_document_caret_range_from_point ( + document, point.x - scroll_left, point.y - scroll_top); + + /* The point is outside the viewport, scroll to it. */ + if (!range) { + WebKitDOMDOMWindow *dom_window = NULL; + + dom_window = webkit_dom_document_get_default_view (document); + webkit_dom_dom_window_scroll_to (dom_window, point.x, point.y); + + scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body)); + scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body)); + range = webkit_dom_document_caret_range_from_point ( + document, point.x - scroll_left, point.y - scroll_top); + g_clear_object (&dom_window); + } + + return range; +} + +static void +restore_selection_to_history_event_state (EEditorPage *editor_page, + EEditorSelection selection_state) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMElement *element, *tmp; + WebKitDOMRange *range = NULL; + gboolean was_collapsed = FALSE; + + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + /* Restore the selection how it was before the event occured. */ + range = get_range_for_point (document, selection_state.start); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + + was_collapsed = selection_state.start.x == selection_state.end.x; + was_collapsed = was_collapsed && selection_state.start.y == selection_state.end.y; + if (was_collapsed) { + g_clear_object (&dom_selection); + return; + } + + e_editor_dom_selection_save (editor_page); + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + remove_node (WEBKIT_DOM_NODE (element)); + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + webkit_dom_element_remove_attribute (element, "id"); + + range = get_range_for_point (document, selection_state.end); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + + e_editor_dom_selection_save (editor_page); + + tmp = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + remove_node (WEBKIT_DOM_NODE (tmp)); + + webkit_dom_element_set_id ( + element, "-x-evo-selection-start-marker"); + + e_editor_dom_selection_restore (editor_page); + + g_clear_object (&dom_selection); +} + +#if d(1)+0 +static void +print_node_inner_html (WebKitDOMNode *node) +{ + gchar *inner_html; + + if (!node) { + printf (" none\n"); + return; + } + + inner_html = dom_get_node_inner_html (node); + + printf (" '%s'\n", inner_html); + + g_free (inner_html); +} + +static void +print_history_event (EEditorHistoryEvent *event) +{ + if (event->type != HISTORY_START && event->type != HISTORY_AND) { + printf (" HISTORY EVENT: %d ; \n", event->type); + printf (" before: start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n", + event->before.start.x, event->before.start.y, event->before.end.x, event->before.end.y); + printf (" after: start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n", + event->after.start.x, event->after.start.y, event->after.end.x, event->after.end.y); + } + switch (event->type) { + case HISTORY_DELETE: + case HISTORY_INPUT: + case HISTORY_REMOVE_LINK: + case HISTORY_SMILEY: + case HISTORY_IMAGE: + case HISTORY_CITATION_SPLIT: + print_node_inner_html (WEBKIT_DOM_NODE (event->data.fragment)); + break; + case HISTORY_ALIGNMENT: + case HISTORY_BLOCK_FORMAT: + case HISTORY_BOLD: + case HISTORY_FONT_SIZE: + case HISTORY_INDENT: + case HISTORY_ITALIC: + case HISTORY_MONOSPACE: + case HISTORY_UNDERLINE: + case HISTORY_STRIKETHROUGH: + case HISTORY_WRAP: + printf (" from %d to %d ;\n", event->data.style.from, event->data.style.to); + break; + case HISTORY_PASTE: + case HISTORY_PASTE_AS_TEXT: + case HISTORY_PASTE_QUOTED: + case HISTORY_INSERT_HTML: + printf (" pasting: '%s' ; \n", event->data.string.to); + break; + case HISTORY_HRULE_DIALOG: + case HISTORY_IMAGE_DIALOG: + case HISTORY_LINK_DIALOG: + case HISTORY_CELL_DIALOG: + case HISTORY_TABLE_DIALOG: + case HISTORY_PAGE_DIALOG: + case HISTORY_UNQUOTE: + print_node_inner_html (event->data.dom.from); + print_node_inner_html (event->data.dom.to); + break; + case HISTORY_FONT_COLOR: + case HISTORY_REPLACE: + case HISTORY_REPLACE_ALL: + printf (" from '%s' to '%s';\n", event->data.string.from, event->data.string.to); + break; + case HISTORY_START: + printf (" HISTORY START\n"); + break; + case HISTORY_AND: + printf (" HISTORY AND\n"); + break; + default: + printf (" Unknown history type\n"); + } +} + +static void +print_history (EEditorUndoRedoManager *manager) +{ + printf ("-------------------\nWHOLE HISTORY STACK\n"); + if (manager->priv->history) { + g_list_foreach ( + manager->priv->history, (GFunc) print_history_event, NULL); + } + printf ("-------------------\n"); +} + +static void +print_undo_events (EEditorUndoRedoManager *manager) +{ + GList *item = manager->priv->history; + + printf ("------------------\nUNDO HISTORY STACK\n"); + if (!item || !item->next) { + printf ("------------------\n"); + return; + } + + print_history_event (item->data); + item = item->next; + while (item) { + print_history_event (item->data); + item = item->next; + } + + printf ("------------------\n"); +} + +static void +print_redo_events (EEditorUndoRedoManager *manager) +{ + GList *item = manager->priv->history; + + printf ("------------------\nREDO HISTORY STACK\n"); + if (!item || !item->prev) { + printf ("------------------\n"); + return; + } + + item = item->prev; + while (item) { + print_history_event (item->data); + item = item->prev; + } + + printf ("------------------\n"); +} +#endif + +static gboolean +event_selection_was_collapsed (EEditorHistoryEvent *ev) +{ + return (ev->before.start.x == ev->before.end.x) && (ev->before.start.y == ev->before.end.y); +} + +static WebKitDOMNode * +split_node_into_two (WebKitDOMNode *item, + gint level) +{ + gint current_level = 1; + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + WebKitDOMNode *parent, *prev_parent = NULL, *tmp = NULL; + + document = webkit_dom_node_get_owner_document (item); + fragment = webkit_dom_document_create_document_fragment (document); + + tmp = item; + parent = webkit_dom_node_get_parent_node (item); + while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + WebKitDOMNode *clone, *first_child, *insert_before = NULL, *sibling; + + first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + clone = webkit_dom_node_clone_node_with_error (parent, FALSE, NULL); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (fragment), clone, first_child, NULL); + + if (first_child) + insert_before = webkit_dom_node_get_first_child (first_child); + + while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child))) + webkit_dom_node_insert_before (first_child, sibling, insert_before, NULL); + + while ((sibling = webkit_dom_node_get_next_sibling (tmp))) + webkit_dom_node_append_child (clone, sibling, NULL); + + webkit_dom_node_insert_before ( + clone, tmp, webkit_dom_node_get_first_child (clone), NULL); + + prev_parent = parent; + tmp = webkit_dom_node_get_next_sibling (parent); + parent = webkit_dom_node_get_parent_node (parent); + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + insert_before = webkit_dom_node_get_first_child (first_child); + while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child))) { + webkit_dom_node_insert_before ( + first_child, sibling, insert_before, NULL); + } + } + + if (current_level >= level && level >= 0) + break; + + current_level++; + } + + if (prev_parent) { + tmp = webkit_dom_node_insert_before ( + parent, + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), + webkit_dom_node_get_next_sibling (prev_parent), + NULL); + remove_node_if_empty (prev_parent); + } + + return tmp; +} + +static void +undo_delete (EEditorPage *editor_page, + EEditorHistoryEvent *event) +{ + gboolean empty, single_block; + gchar *content; + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + WebKitDOMElement *element; + WebKitDOMNode *first_child, *fragment; + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + fragment = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (event->data.fragment), TRUE, NULL); + first_child = webkit_dom_node_get_first_child (fragment); + + content = webkit_dom_node_get_text_content (fragment); + empty = content && !*content; + g_free (content); + + /* Tabulator */ + single_block = event->type == HISTORY_INPUT; + single_block = single_block && event->before.start.x != 0 && event->before.end.y != 0; + + if (!single_block) { + /* One block delete */ + if ((single_block = WEBKIT_DOM_IS_ELEMENT (first_child))) + single_block = element_has_id (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-selection-start-marker"); + else + single_block = WEBKIT_DOM_IS_TEXT (first_child); + } + + /* Delete or BackSpace pressed in the beginning of a block or on its end. */ + if (event->type == HISTORY_DELETE && !single_block && + g_object_get_data (G_OBJECT (event->data.fragment), "history-concatenating-blocks")) { + WebKitDOMNode *node, *block; + + range = get_range_for_point (document, event->after.start); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + + node = webkit_dom_range_get_end_container (range, NULL); + block = e_editor_dom_get_parent_block_node_from_child (node); + + if (webkit_dom_document_fragment_query_selector (event->data.fragment, ".-x-evo-quoted", NULL)) { + while ((node = webkit_dom_node_get_first_child (fragment))) { + if (WEBKIT_DOM_IS_ELEMENT (node) && + webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (node), ".-x-evo-quoted", NULL)) + + if (e_editor_dom_get_citation_level (block, FALSE) > 0) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + node, + block, + NULL); + } else { + WebKitDOMNode *next_block; + + next_block = webkit_dom_node_get_next_sibling (block); + while (next_block && e_editor_dom_node_is_citation_node (next_block)) + next_block = webkit_dom_node_get_first_child (next_block); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (next_block), + node, + next_block, + NULL); + } + else { + if (e_editor_dom_get_citation_level (block, FALSE) > 0) { + WebKitDOMNode *next_node; + + if ((next_node = split_node_into_two (block, -1))) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (next_node), + node, + next_node, + NULL); + else + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + node, + block, + NULL); + } else + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + node, + block, + NULL); + } + } + } else { + while ((node = webkit_dom_node_get_first_child (fragment))) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + node, + block, + NULL); + } + } + + remove_node (block); + + g_clear_object (&range); + g_clear_object (&dom_selection); + + restore_selection_to_history_event_state (editor_page, event->before); + + e_editor_dom_force_spell_check_in_viewport (editor_page); + + return; + } + + /* Redoing Return key press */ + if (event->type == HISTORY_INPUT && (empty || + g_object_get_data (G_OBJECT (event->data.fragment), "history-return-key"))) { + if (e_editor_dom_key_press_event_process_return_key (editor_page)) { + e_editor_dom_body_key_up_event_process_return_key (editor_page); + } else { + WebKitDOMElement *element; + WebKitDOMNode *next_sibling; + + range = get_range_for_point (document, event->before.start); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + + e_editor_dom_selection_save (editor_page); + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + if (next_sibling && !(WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling))) { + split_node_into_two (WEBKIT_DOM_NODE (element), 1); + } else { + WebKitDOMNode *block; + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (element)); + dom_remove_selection_markers (document); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (block), + fragment, + webkit_dom_node_get_next_sibling (block), + NULL); + } + e_editor_dom_selection_restore (editor_page); + } + + e_editor_page_set_return_key_pressed (editor_page, TRUE); + e_editor_dom_check_magic_links (editor_page, FALSE); + e_editor_page_set_return_key_pressed (editor_page, FALSE); + e_editor_dom_force_spell_check_in_viewport (editor_page); + + g_clear_object (&dom_selection); + + return; + } + + if (!single_block) { + if (WEBKIT_DOM_IS_ELEMENT (first_child) && + !(WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (first_child) || + WEBKIT_DOM_IS_HTML_PRE_ELEMENT (first_child) || + WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (first_child))) + single_block = TRUE; + } + + /* Multi block delete */ + if (WEBKIT_DOM_IS_ELEMENT (first_child) && !single_block) { + gboolean delete; + WebKitDOMElement *signature; + WebKitDOMNode *node, *current_block, *last_child; + WebKitDOMNode *next_block, *insert_before; + + range = get_range_for_point (document, event->after.start); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + e_editor_dom_selection_save (editor_page); + + if ((element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker"))) { + WebKitDOMNode *next_sibling; + + if ((next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element))) && + WEBKIT_DOM_IS_CHARACTER_DATA (next_sibling) && + webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (next_sibling)) == 1) { + gchar *data; + + data = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (next_sibling)); + if (data && *data == ' ') { + WebKitDOMElement *hidden_space; + + hidden_space = webkit_dom_document_create_element (document, "span", NULL); + webkit_dom_element_set_attribute ( + hidden_space, "data-hidden-space", "", NULL); + remove_node (next_sibling); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + WEBKIT_DOM_NODE (hidden_space), + webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (element)), + NULL); + } + g_free (data); + } + } + + element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"); + + /* Get the last block in deleted content. */ + last_child = webkit_dom_node_get_last_child (fragment); + while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child)) + last_child = webkit_dom_node_get_last_child (last_child); + + /* All the nodes that are in current block after the caret position + * belongs on the end of the deleted content. */ + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + + /* FIXME Ugly hack */ + /* If the selection ended in signature, the structure will be broken + * thus we saved the whole signature into deleted fragment and we will + * restore the whole signature, but we need to remove the rest of the + * signature that's left after delete to avoid duplications. */ + signature = webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL); + delete = signature && webkit_dom_node_contains (WEBKIT_DOM_NODE (signature), WEBKIT_DOM_NODE (element)); + if (!delete) { + WebKitDOMNode *tmp_node; + + tmp_node = webkit_dom_node_get_last_child (fragment); + delete = tmp_node && WEBKIT_DOM_IS_ELEMENT (tmp_node) && + element_has_class (WEBKIT_DOM_ELEMENT (tmp_node), "-x-evo-signature-wrapper"); + } + + current_block = e_editor_dom_get_parent_block_node_from_child (WEBKIT_DOM_NODE (element)); + + while (node) { + WebKitDOMNode *next_sibling, *parent_node; + + next_sibling = webkit_dom_node_get_next_sibling (node); + parent_node = webkit_dom_node_get_parent_node (node); + /* Check if the whole element was deleted. If so replace it. */ + if (!next_sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (node) && + !webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element))) { + WebKitDOMNode *tmp_node; + WebKitDOMElement *tmp_element; + + tmp_node = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (tmp_node), + fragment, + tmp_node, + NULL); + + /* Remove empty blockquotes, if presented. */ + tmp_element = webkit_dom_document_query_selector ( + document, "blockquote[type=cite]:empty", NULL); + if (tmp_element) + remove_node (WEBKIT_DOM_NODE (tmp_element)); + + e_editor_dom_merge_siblings_if_necessary (editor_page, event->data.fragment); + + tmp_node = webkit_dom_node_get_last_child (last_child); + if (tmp_node && WEBKIT_DOM_IS_ELEMENT (tmp_node) && + element_has_class (WEBKIT_DOM_ELEMENT (tmp_node), "-x-evo-quoted")) { + webkit_dom_node_append_child ( + last_child, + WEBKIT_DOM_NODE ( + webkit_dom_document_create_element ( + document, "br", NULL)), + NULL); + } + + dom_remove_selection_markers (document); + + restore_selection_to_history_event_state (editor_page, event->before); + + e_editor_dom_force_spell_check_in_viewport (editor_page); + + g_clear_object (&dom_selection); + + return; + } else if (!next_sibling && !webkit_dom_node_is_same_node (parent_node, current_block)) + next_sibling = webkit_dom_node_get_next_sibling (parent_node); + + if (delete) + remove_node (node); + else + webkit_dom_node_append_child (last_child, node, NULL); + node = next_sibling; + } + + /* Get the first block in deleted content. */ + while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (first_child)) + first_child = webkit_dom_node_get_first_child (first_child); + + /* All the nodes that are in the first block of the deleted content + * belongs to the current block right after the caret position. */ + while ((node = webkit_dom_node_get_first_child (first_child))) + webkit_dom_node_append_child (current_block, node, NULL); + + next_block = webkit_dom_node_get_next_sibling (current_block); + insert_before = next_block; + + if (!insert_before) { + WebKitDOMNode *parent = NULL; + + parent = current_block; + while ((parent = webkit_dom_node_get_parent_node (parent)) && + !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + insert_before = webkit_dom_node_get_next_sibling (parent); + if (insert_before) + break; + } + } + + /* Split a BLOCKQUOTE if the deleted content started with BLOCKQUOTE */ + if (insert_before && + WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT ( + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment))) && + e_editor_dom_get_citation_level (insert_before, FALSE > 0)) + insert_before = split_node_into_two (insert_before, -1); + + /* Remove the first block from deleted content as its content was already + * moved to the right place. */ + remove_node (first_child); + + /* Insert the deleted content. */ + if (insert_before) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (fragment), + insert_before, + NULL); + else + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE ( + webkit_dom_document_get_body (document)), + WEBKIT_DOM_NODE (fragment), + NULL); + + e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (current_block)); + + if (WEBKIT_DOM_IS_ELEMENT (last_child)) + e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (last_child)); + + e_editor_dom_merge_siblings_if_necessary (editor_page, event->data.fragment); + + /* If undoing drag and drop where the whole line was moved we need + * to correct the selection. */ + if (g_object_get_data (G_OBJECT (event->data.fragment), "history-drag-and-drop") && + (element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker"))) { + WebKitDOMNode *prev_block; + + prev_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + if ((prev_block = webkit_dom_node_get_previous_sibling (prev_block))) + webkit_dom_node_append_child ( + prev_block, WEBKIT_DOM_NODE (element), NULL); + } + + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_in_viewport (editor_page); + } else { + gboolean empty_text = FALSE, was_link = FALSE; + WebKitDOMNode *prev_sibling, *next_sibling, *nd; + WebKitDOMNode *parent; + + element = webkit_dom_document_create_element (document, "span", NULL); + + /* Create temporary node on the selection where the delete occured. */ + if (webkit_dom_document_fragment_query_selector (event->data.fragment, ".Apple-tab-span", NULL)) + range = get_range_for_point (document, event->before.start); + else + range = get_range_for_point (document, event->after.start); + + webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + + nd = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + if (nd && WEBKIT_DOM_IS_TEXT (nd)) { + gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (nd)); + glong length = webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (nd)); + + /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE + * character as when we will remove it paragraph will collapse. */ + if (length > 1) { + if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE)) + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (nd), 0, 1, "", NULL); + else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE)) + webkit_dom_character_data_replace_data ( + WEBKIT_DOM_CHARACTER_DATA (nd), length - 1, 1, "", NULL); + } else if (length == 0) + empty_text = TRUE; + + g_free (text); + } + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + if (!nd || empty_text) { + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + WEBKIT_DOM_NODE (element), + parent, + NULL); + } + + /* Insert the deleted content back to the body. */ + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) { + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (first_child)) { + WebKitDOMNode *child; + + while ((child = webkit_dom_node_get_first_child (first_child))) + webkit_dom_node_append_child (parent, child, NULL); + + remove_node (first_child); + + was_link = TRUE; + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + fragment, + webkit_dom_node_get_next_sibling (parent), + NULL); + } else { + if (g_object_get_data (G_OBJECT (event->data.fragment), "history-removing-from-anchor") || + !event_selection_was_collapsed (event)) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + fragment, + WEBKIT_DOM_NODE (element), + NULL); + } else { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + fragment, + webkit_dom_node_get_next_sibling (parent), + NULL); + } + } + } else { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + fragment, + WEBKIT_DOM_NODE (element), + NULL); + } + + webkit_dom_node_normalize (parent); + prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + if (prev_sibling && next_sibling) { + WebKitDOMNode *clone_prev, *clone_next; + + clone_prev = webkit_dom_node_clone_node_with_error (prev_sibling, FALSE, NULL); + clone_next = webkit_dom_node_clone_node_with_error (next_sibling, FALSE, NULL); + + if (webkit_dom_node_is_equal_node (clone_prev, clone_next)) { + WebKitDOMNode *child; + + while ((child = webkit_dom_node_get_first_child (next_sibling))) + webkit_dom_node_append_child (prev_sibling, child, NULL); + + remove_node (next_sibling); + } + } + + remove_node (WEBKIT_DOM_NODE (element)); + + if (event->type == HISTORY_DELETE && !e_editor_page_get_html_mode (editor_page)) { + WebKitDOMNode *current_block; + + current_block = e_editor_dom_get_parent_block_node_from_child (parent); + if (e_editor_dom_get_citation_level (current_block, FALSE) > 0) + e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (current_block)); + } + + /* If the selection markers are presented restore the selection, + * otherwise the selection was not collapsed so select the deleted + * content as it was before the delete occurred. */ + if (webkit_dom_document_fragment_query_selector (event->data.fragment, "span#-x-evo-selection-start-marker", NULL)) + e_editor_dom_selection_restore (editor_page); + else + restore_selection_to_history_event_state (editor_page, event->before); + + if (event->type != HISTORY_INPUT) { + if (e_editor_page_get_magic_smileys_enabled (editor_page)) + e_editor_dom_check_magic_smileys (editor_page); + if (!was_link && e_editor_page_get_magic_links_enabled (editor_page)) + e_editor_dom_check_magic_links (editor_page, FALSE); + } + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + } + + g_clear_object (&dom_selection); +} + +static void +redo_delete (EEditorPage *editor_page, + EEditorHistoryEvent *event) +{ + EEditorUndoRedoManager *manager; + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment = event->data.fragment; + WebKitDOMNode *node; + WebKitDOMHTMLElement *body; + gboolean delete_key, control_key, html_mode; + glong length = 1; + gint ii; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + document = e_editor_page_get_document (editor_page); + html_mode = e_editor_page_get_html_mode (editor_page); + restore_selection_to_history_event_state (editor_page, event->before); + + delete_key = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), "history-delete-key")); + control_key = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), "history-control-key")); + + body = webkit_dom_document_get_body (document); + + if (!html_mode) + e_editor_dom_set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), TRUE); + + if (!delete_key && e_editor_dom_key_press_event_process_backspace_key (editor_page)) + goto out; + + if (e_editor_dom_key_press_event_process_delete_or_backspace_key (editor_page, ~0, 0, delete_key)) + goto out; + + if (control_key) { + gchar *text_content; + + text_content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (fragment)); + length = g_utf8_strlen (text_content, -1); + control_key = length > 1; + + g_free (text_content); + } + + /* If concatenating two blocks with pressing Delete on the end + * of the previous one and the next node contain content that + * is wrapped on multiple lines, the last line will by separated + * by WebKit to the separate block. To avoid it let's remove + * all quoting and wrapping from the next paragraph. */ + if (delete_key && + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), "history-concatenating-blocks"))) { + WebKitDOMNode *current_block, *next_block, *node; + WebKitDOMRange *range = NULL; + + range = e_editor_dom_get_current_range (editor_page); + node = webkit_dom_range_get_end_container (range, NULL); + g_clear_object (&range); + current_block = e_editor_dom_get_parent_block_node_from_child (node); + if (e_editor_dom_get_citation_level (current_block, FALSE) > 0 && + (next_block = webkit_dom_node_get_next_sibling (current_block))) { + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (next_block)); + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (next_block)); + } + } + + for (ii = 0; ii < length; ii++) { + e_editor_dom_exec_command (editor_page, + delete_key ? E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE : + E_CONTENT_EDITOR_COMMAND_DELETE, + NULL); + } + + /* Really don't know why, but when the selection marker nodes were in + * anchors then we need to do an extra delete command otherwise we will + * end with two blocks split in half. */ + node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + while ((node = webkit_dom_node_get_first_child (node))) { + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) { + e_editor_dom_exec_command (editor_page, + E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE, + NULL); + break; + } + } + + node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)); + while ((node = webkit_dom_node_get_last_child (node))) { + if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) { + e_editor_dom_exec_command (editor_page, + E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE, + NULL); + break; + } + } + + out: + e_editor_page_set_dont_save_history_in_body_input (editor_page, TRUE); + e_editor_undo_redo_manager_set_operation_in_progress (manager, FALSE); + e_editor_dom_body_input_event_process (editor_page, NULL); + e_editor_page_set_dont_save_history_in_body_input (editor_page, FALSE); + e_editor_undo_redo_manager_set_operation_in_progress (manager, TRUE); + e_editor_page_set_renew_history_after_coordinates (editor_page, FALSE); + e_editor_dom_body_key_up_event_process_backspace_or_delete (editor_page, delete_key); + e_editor_page_set_renew_history_after_coordinates (editor_page, TRUE); + if (!html_mode) + e_editor_dom_set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), FALSE); + + restore_selection_to_history_event_state (editor_page, event->after); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); +} + +typedef void (*SelectionStyleChangeFunc) (EEditorPage *editor_page, gint style); + +static void +undo_redo_style_change (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + SelectionStyleChangeFunc func; + + switch (event->type) { + case HISTORY_ALIGNMENT: + func = (SelectionStyleChangeFunc) e_editor_dom_selection_set_alignment; + break; + case HISTORY_BOLD: + func = e_editor_dom_selection_set_bold; + break; + case HISTORY_BLOCK_FORMAT: + func = (SelectionStyleChangeFunc) e_editor_dom_selection_set_block_format; + break; + case HISTORY_FONT_SIZE: + func = (SelectionStyleChangeFunc) e_editor_dom_selection_set_font_size; + break; + case HISTORY_ITALIC: + func = e_editor_dom_selection_set_italic; + break; + case HISTORY_MONOSPACE: + func = e_editor_dom_selection_set_monospace; + break; + case HISTORY_STRIKETHROUGH: + func = e_editor_dom_selection_set_strikethrough; + break; + case HISTORY_UNDERLINE: + func = e_editor_dom_selection_set_underline; + break; + default: + return; + } + + restore_selection_to_history_event_state (editor_page, undo ? event->after : event->before); + + func (editor_page, undo ? event->data.style.from : event->data.style.to); + + restore_selection_to_history_event_state (editor_page, undo ? event->before : event->after); +} + +static void +undo_redo_indent (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + gboolean was_indent = FALSE; + + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + was_indent = event->data.style.from && event->data.style.to; + + if ((undo && was_indent) || (!undo && !was_indent)) + e_editor_dom_selection_unindent (editor_page); + else + e_editor_dom_selection_indent (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->before); +} + +static void +undo_redo_font_color (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + e_editor_dom_exec_command (editor_page, + E_CONTENT_EDITOR_COMMAND_FORE_COLOR, + undo ? event->data.string.from : event->data.string.to); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->before); +} + +static void +undo_redo_wrap (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + if (undo) { + WebKitDOMNode *node; + WebKitDOMElement *element; + WebKitDOMRange *range = NULL; + + range = e_editor_dom_get_current_range (editor_page); + node = webkit_dom_range_get_common_ancestor_container (range, NULL); + g_clear_object (&range); + element = get_parent_block_element (WEBKIT_DOM_NODE (node)); + webkit_dom_element_remove_attribute (element, "data-user-wrapped"); + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (element)); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + } else + e_editor_dom_selection_wrap (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->before); +} + +static void +undo_redo_page_dialog (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + WebKitDOMNamedNodeMap *attributes = NULL, *attributes_history = NULL; + gint length, length_history, ii, jj; + + document = e_editor_page_get_document (editor_page); + body = webkit_dom_document_get_body (document); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + if (undo) { + attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); + attributes_history = webkit_dom_element_get_attributes ( + WEBKIT_DOM_ELEMENT (event->data.dom.from)); + } else { + attributes_history = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); + attributes = webkit_dom_element_get_attributes ( + WEBKIT_DOM_ELEMENT (event->data.dom.to)); + } + + length = webkit_dom_named_node_map_get_length (attributes); + length_history = webkit_dom_named_node_map_get_length (attributes_history); + for (ii = length - 1; ii >= 0; ii--) { + gchar *name; + WebKitDOMNode *attr; + gboolean replaced = FALSE; + + attr = webkit_dom_named_node_map_item (attributes, ii); + name = webkit_dom_node_get_local_name (attr); + + for (jj = length_history - 1; jj >= 0; jj--) { + gchar *name_history; + WebKitDOMNode *attr_history; + + attr_history = webkit_dom_named_node_map_item (attributes_history, jj); + name_history = webkit_dom_node_get_local_name (attr_history); + if (g_strcmp0 (name, name_history) == 0) { + WebKitDOMNode *attr_clone; + + attr_clone = webkit_dom_node_clone_node_with_error ( + undo ? attr_history : attr, TRUE, NULL); + webkit_dom_element_set_attribute_node ( + WEBKIT_DOM_ELEMENT (body), + WEBKIT_DOM_ATTR (attr_clone), + NULL); + + /* Link color has to replaced in HEAD as well. */ + if (g_strcmp0 (name, "link") == 0) { + gchar *value; + + value = webkit_dom_node_get_node_value (attr_clone); + e_editor_dom_set_link_color (editor_page, value); + g_free (value); + } else if (g_strcmp0 (name, "vlink") == 0) { + gchar *value; + + value = webkit_dom_node_get_node_value (attr_clone); + e_editor_dom_set_visited_link_color (editor_page, value); + g_free (value); + } + replaced = TRUE; + } + g_free (name_history); + g_clear_object (&attr_history); + if (replaced) + break; + } + + if (!replaced) { + if (undo) { + webkit_dom_element_remove_attribute_node ( + WEBKIT_DOM_ELEMENT (body), + WEBKIT_DOM_ATTR (attr), + NULL); + } else { + webkit_dom_element_set_attribute_node ( + WEBKIT_DOM_ELEMENT (body), + WEBKIT_DOM_ATTR ( + webkit_dom_node_clone_node_with_error (attr, TRUE, NULL)), + NULL); + } + } + g_free (name); + g_object_unref (attr); + } + g_clear_object (&attributes); + g_clear_object (&attributes_history); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->before); +} + +static void +undo_redo_hrule_dialog (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + + document = e_editor_page_get_document (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + e_editor_dom_selection_save (editor_page); + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + if (undo) { + WebKitDOMNode *node; + WebKitDOMElement *parent; + + parent = get_parent_block_element (WEBKIT_DOM_NODE (element)); + if (event->data.dom.from) + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent)); + else + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)); + + if (node && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node)) { + if (!event->data.dom.from) + remove_node (node); + else + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (node), + webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, NULL), + node, + NULL); + } + } else { + WebKitDOMNode *node; + WebKitDOMElement *parent; + + parent = get_parent_block_element (WEBKIT_DOM_NODE (element)); + + if (event->data.dom.from) { + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)); + + if (node && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node)) + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (node), + webkit_dom_node_clone_node_with_error (event->data.dom.to, TRUE, NULL), + node, + NULL); + } else { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)), + event->data.dom.to, + webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)), + NULL); + } + } + + if (undo) { + dom_remove_selection_markers (document); + restore_selection_to_history_event_state (editor_page, event->before); + } else + e_editor_dom_selection_restore (editor_page); +} + +static void +undo_redo_image_dialog (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + WebKitDOMNode *sibling, *image = NULL; + + document = e_editor_page_get_document (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + e_editor_dom_selection_save (editor_page); + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)); + if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) { + if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling)) + image = sibling; + else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper")) + image = webkit_dom_node_get_first_child (sibling); + } + + if (!image) { + element = WEBKIT_DOM_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element))); + sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) { + if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling)) + image = sibling; + else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper")) + image = webkit_dom_node_get_first_child (sibling); + } + } + + if (!image) + return; + + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (image), + webkit_dom_node_clone_node_with_error (undo ? event->data.dom.from : event->data.dom.to, TRUE, NULL), + image, + NULL); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->before); + else + e_editor_dom_selection_restore (editor_page); +} + +static void +undo_redo_link_dialog (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + WebKitDOMElement *anchor, *element; + + document = e_editor_page_get_document (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + else + restore_selection_to_history_event_state (editor_page, event->before); + + e_editor_dom_selection_save (editor_page); + + element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"); + if (!element) + return; + + anchor = dom_node_find_parent_element (WEBKIT_DOM_NODE (element), "A"); + if (undo) { + if (anchor) { + if (!event->data.dom.from) + remove_node (WEBKIT_DOM_NODE (anchor)); + else + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (anchor)), + webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, NULL), + WEBKIT_DOM_NODE (anchor), + NULL); + } + } else { + if (!event->data.dom.to) { + if (anchor) + remove_node (WEBKIT_DOM_NODE (anchor)); + } else { + if (WEBKIT_DOM_IS_ELEMENT (event->data.dom.from) && anchor) { + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (anchor)), + webkit_dom_node_clone_node_with_error (event->data.dom.to, TRUE, NULL), + WEBKIT_DOM_NODE (anchor), + NULL); + } else { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + webkit_dom_node_clone_node_with_error (event->data.dom.to, TRUE, NULL), + WEBKIT_DOM_NODE (element), + NULL); + + if (event->data.dom.from) + e_editor_dom_exec_command (editor_page, + E_CONTENT_EDITOR_COMMAND_DELETE, NULL); + } + } + } + + if (undo) + restore_selection_to_history_event_state (editor_page, event->before); + else + e_editor_dom_selection_restore (editor_page); +} + +static void +undo_redo_table_dialog (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + WebKitDOMElement *table, *element; + + document = e_editor_page_get_document (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + e_editor_dom_selection_save (editor_page); + element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"); + if (!element) + return; + + table = dom_node_find_parent_element (WEBKIT_DOM_NODE (element), "TABLE"); + + if (!table) { + if ((!event->data.dom.to && undo) || (!event->data.dom.from && !undo)) { + WebKitDOMElement *parent; + + parent = get_parent_block_element (WEBKIT_DOM_NODE (element)); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)), + webkit_dom_node_clone_node_with_error (undo ? event->data.dom.from : event->data.dom.to, TRUE, NULL), + WEBKIT_DOM_NODE (parent), + NULL); + restore_selection_to_history_event_state (editor_page, event->before); + return; + } else + return; + } + + if (undo) { + if (!event->data.dom.from) + remove_node (WEBKIT_DOM_NODE (table)); + else + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)), + webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, NULL), + WEBKIT_DOM_NODE (table), + NULL); + } else { + if (!event->data.dom.to) + remove_node (WEBKIT_DOM_NODE (table)); + else + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)), + webkit_dom_node_clone_node_with_error (event->data.dom.to, TRUE, NULL), + WEBKIT_DOM_NODE (table), + NULL); + } + + if (undo) + restore_selection_to_history_event_state (editor_page, event->before); + else + e_editor_dom_selection_restore (editor_page); +} + +static void +undo_redo_table_input (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMElement *element; + WebKitDOMNode *node; + WebKitDOMRange *range = NULL; + + document = e_editor_page_get_document (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (!webkit_dom_dom_selection_get_range_count (dom_selection)) { + g_clear_object (&dom_selection); + return; + } + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + g_clear_object (&dom_selection); + + /* Find if writing into table. */ + node = webkit_dom_range_get_start_container (range, NULL); + if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) + element = WEBKIT_DOM_ELEMENT (node); + else + element = get_parent_block_element (node); + + g_clear_object (&range); + + /* If writing to table we have to create different history event. */ + if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (element)) + return; + + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + webkit_dom_node_clone_node_with_error (undo ? event->data.dom.from : event->data.dom.to, TRUE, NULL), + WEBKIT_DOM_NODE (element), + NULL); + + e_editor_dom_selection_restore (editor_page); +} + +static void +undo_redo_paste (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + + document = e_editor_page_get_document (editor_page); + + if (undo) { + if (event->type == HISTORY_PASTE_QUOTED) { + WebKitDOMElement *tmp; + WebKitDOMNode *parent; + + restore_selection_to_history_event_state (editor_page, event->after); + + e_editor_dom_selection_save (editor_page); + tmp = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (!tmp) + return; + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp)); + while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent))) + parent = webkit_dom_node_get_parent_node (parent); + + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (parent), + WEBKIT_DOM_NODE (e_editor_dom_prepare_paragraph (editor_page, TRUE)), + parent, + NULL); + + e_editor_dom_selection_restore (editor_page); + } else { + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMElement *element, *tmp; + WebKitDOMRange *range = NULL; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + /* Restore the selection how it was before the event occured. */ + range = get_range_for_point (document, event->before.start); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + + e_editor_dom_selection_save (editor_page); + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + remove_node (WEBKIT_DOM_NODE (element)); + + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + webkit_dom_element_remove_attribute (element, "id"); + + range = get_range_for_point (document, event->after.start); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + g_clear_object (&dom_selection); + + e_editor_dom_selection_save (editor_page); + + tmp = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + remove_node (WEBKIT_DOM_NODE (tmp)); + + webkit_dom_element_set_id ( + element, "-x-evo-selection-start-marker"); + + e_editor_dom_selection_restore (editor_page); + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + } + } else { + restore_selection_to_history_event_state (editor_page, event->before); + + if (event->type == HISTORY_PASTE) + e_editor_dom_convert_and_insert_html_into_selection (editor_page, event->data.string.to, FALSE); + else if (event->type == HISTORY_PASTE_QUOTED) + e_editor_dom_quote_and_insert_text_into_selection (editor_page, event->data.string.to, FALSE); + else if (event->type == HISTORY_INSERT_HTML) + e_editor_dom_insert_html (editor_page, event->data.string.to); + else + e_editor_dom_convert_and_insert_html_into_selection (editor_page, event->data.string.to, FALSE); + /* e_editor_selection_insert_as_text (selection, event->data.string.to); */ + } +} + +static void +undo_redo_image (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMRange *range = NULL; + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + if (undo) { + WebKitDOMElement *element; + WebKitDOMNode *node; + + range = get_range_for_point (document, event->before.start); + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + + e_editor_dom_selection_save (editor_page); + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)); + + if (WEBKIT_DOM_IS_ELEMENT (node)) + if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-resizable-wrapper") || + element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-smiley-wrapper")) + remove_node (node); + e_editor_dom_selection_restore (editor_page); + } else { + WebKitDOMElement *element; + + range = get_range_for_point (document, event->before.start); + /* Create temporary node on the selection where the delete occured. */ + webkit_dom_dom_selection_remove_all_ranges (dom_selection); + webkit_dom_dom_selection_add_range (dom_selection, range); + g_clear_object (&range); + + e_editor_dom_selection_save (editor_page); + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + /* Insert the deleted content back to the body. */ + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (event->data.fragment), TRUE, NULL), + WEBKIT_DOM_NODE (element), + NULL); + + e_editor_dom_selection_restore (editor_page); + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + } + + g_clear_object (&dom_selection); +} + +static void +undo_redo_replace (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + + document = e_editor_page_get_document (editor_page); + + restore_selection_to_history_event_state (editor_page, undo ? event->after : event->before); + + if (undo) { + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + + webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "word"); + g_clear_object (&dom_selection); + } + + e_editor_dom_exec_command (editor_page, + E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, + undo ? event->data.string.from : event->data.string.to); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); + + restore_selection_to_history_event_state (editor_page, undo ? event->before : event->after); +} + +static void +undo_redo_replace_all (EEditorUndoRedoManager *manager, + EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + + document = e_editor_page_get_document (editor_page); + + if (undo) { + if (event->type == HISTORY_REPLACE) { + undo_redo_replace (editor_page, event, undo); + return; + } else { + EEditorHistoryEvent *next_event; + GList *next_item; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + + next_item = manager->priv->history->next; + + while (next_item) { + next_event = next_item->data; + + if (next_event->type != HISTORY_REPLACE) + break; + + if (g_strcmp0 (next_event->data.string.from, event->data.string.from) != 0) + break; + + if (g_strcmp0 (next_event->data.string.to, event->data.string.to) != 0) + break; + + undo_redo_replace (editor_page, next_event, undo); + + next_item = next_item->next; + } + + manager->priv->history = next_item->prev; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); + g_clear_object (&dom_window); + g_clear_object (&dom_selection); + } + } else { + /* Find if this history item is part of HISTORY_REPLACE_ALL. */ + EEditorHistoryEvent *prev_event; + GList *prev_item; + gboolean replace_all = FALSE; + + prev_item = manager->priv->history->prev; + while (prev_item) { + prev_event = prev_item->data; + + if (prev_event->type == HISTORY_REPLACE) + prev_item = prev_item->prev; + else if (prev_event->type == HISTORY_REPLACE_ALL) { + replace_all = TRUE; + break; + } else + break; + } + + if (!replace_all) { + undo_redo_replace (editor_page, event, undo); + return; + } + + prev_item = manager->priv->history->prev; + while (prev_item) { + prev_event = prev_item->data; + + if (prev_event->type == HISTORY_REPLACE) { + undo_redo_replace (editor_page, prev_event, undo); + prev_item = prev_item->prev; + } else + break; + } + + manager->priv->history = prev_item->next; + } +} + +static void +undo_redo_remove_link (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + + document = e_editor_page_get_document (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->after); + + if (undo) { + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMElement *element; + WebKitDOMRange *range = NULL; + + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + /* Select the anchor. */ + webkit_dom_dom_selection_modify (dom_selection, "move", "left", "word"); + webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "word"); + + range = e_editor_dom_get_current_range (editor_page); + element = webkit_dom_document_create_element (document, "SPAN", NULL); + webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL); + g_clear_object (&range); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (event->data.fragment), TRUE, NULL), + WEBKIT_DOM_NODE (element), + NULL); + remove_node (WEBKIT_DOM_NODE (element)); + g_clear_object (&dom_window); + g_clear_object (&dom_selection); + } else + e_editor_dom_selection_unlink (editor_page); + + if (undo) + restore_selection_to_history_event_state (editor_page, event->before); +} + +static void +undo_return_in_empty_list_item (EEditorPage *editor_page, + EEditorHistoryEvent *event) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker; + WebKitDOMNode *parent; + + document = e_editor_page_get_document (editor_page); + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"); + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker)); + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent)) { + WebKitDOMNode *parent_list; + + dom_remove_selection_markers (document); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (parent), + webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (event->data.fragment), TRUE, NULL), + webkit_dom_node_get_next_sibling (parent), + NULL); + + parent_list = parent; + while (node_is_list_or_item (webkit_dom_node_get_parent_node (parent_list))) + parent_list = webkit_dom_node_get_parent_node (parent_list); + + merge_lists_if_possible (parent_list); + } + + e_editor_dom_selection_restore (editor_page); +} + +static gboolean +undo_return_press_after_h_rule (EEditorPage *editor_page, + EEditorHistoryEvent *event) +{ + WebKitDOMDocument *document; + WebKitDOMElement *selection_start_marker, *block; + WebKitDOMNode *node; + + document = e_editor_page_get_document (editor_page); + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + block = get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker)); + node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE( block)); + + if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)) && + WEBKIT_DOM_IS_HTML_HR_ELEMENT (node)) { + + remove_node_if_empty (WEBKIT_DOM_NODE (block)); + restore_selection_to_history_event_state (editor_page, event->before); + + return TRUE; + } + + return FALSE; +} + +static void +undo_input (EEditorUndoRedoManager *manager, + EEditorPage *editor_page, + EEditorHistoryEvent *event) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMNode *node, *tmp_node; + gboolean remove_anchor; + + document = e_editor_page_get_document (editor_page); + dom_window = webkit_dom_document_get_default_view (document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + + restore_selection_to_history_event_state (editor_page, event->after); + + /* Undoing Return press after the HR element */ + if (e_editor_page_get_html_mode (editor_page) && + g_object_get_data (G_OBJECT (event->data.fragment), "history-return-key")) { + if (undo_return_press_after_h_rule (editor_page, event)) { + g_clear_object (&dom_window); + g_clear_object (&dom_selection); + return; + } + } + + webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character"); + if (e_editor_dom_selection_is_citation (editor_page)) { + /* Post processing of quoted text in body_input_event_cb needs to be called. */ + manager->priv->operation_in_progress = FALSE; + e_editor_page_set_dont_save_history_in_body_input (editor_page, TRUE); + } + + /* If we are undoing the text that was appended to the link we have to + * remove the link and make just the plain text from it. */ + node = webkit_dom_dom_selection_get_anchor_node (dom_selection); + node = webkit_dom_node_get_parent_node (node); + remove_anchor = WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node); + if (remove_anchor) { + gchar *text_content; + + text_content = webkit_dom_node_get_text_content (node); + /* Remove the anchor just in case we are undoing the input from + * the end of it. */ + remove_anchor = + g_utf8_strlen (text_content, -1) == + webkit_dom_dom_selection_get_anchor_offset (dom_selection); + g_free (text_content); + } + + e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL); + + if (remove_anchor) { + WebKitDOMNode *child; + + /* Don't ask me why, but I got into the situation where the node + * that I received above was out of the document, and all the + * modifications to it were of course not propagated to it. Let's + * get that node again. */ + node = webkit_dom_dom_selection_get_anchor_node (dom_selection); + node = webkit_dom_node_get_parent_node (node); + while ((child = webkit_dom_node_get_first_child (node))) + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), child, node, NULL); + + remove_node (node); + } + + tmp_node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (event->data.fragment)); + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (tmp_node) && + WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_last_child (tmp_node))) + undo_return_in_empty_list_item (editor_page, event); + + g_clear_object (&dom_window); + g_clear_object (&dom_selection); +} + +static void +undo_redo_citation_split (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + gboolean in_situ = FALSE; + + document = e_editor_page_get_document (editor_page); + + if (event->before.start.x == event->after.start.x && + event->before.start.y == event->after.start.y && + event->before.end.x == event->after.end.x && + event->before.end.y == event->after.end.y) + in_situ = TRUE; + + if (undo) { + WebKitDOMElement *selection_start, *parent; + WebKitDOMNode *citation_before, *citation_after, *child, *last_child, *tmp; + + restore_selection_to_history_event_state (editor_page, event->after); + + e_editor_dom_selection_save (editor_page); + selection_start = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (!selection_start) + return; + + parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start)); + + citation_before = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent)); + if (!e_editor_dom_node_is_citation_node (citation_before)) { + e_editor_dom_selection_restore (editor_page); + return; + } + + citation_after = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)); + if (!e_editor_dom_node_is_citation_node (citation_after)) { + e_editor_dom_selection_restore (editor_page); + return; + } + + /* Get first block in next citation. */ + child = webkit_dom_node_get_first_child (citation_after); + while (child && e_editor_dom_node_is_citation_node (child)) + child = webkit_dom_node_get_first_child (child); + + /* Get last block in previous citation. */ + last_child = webkit_dom_node_get_last_child (citation_before); + while (last_child && e_editor_dom_node_is_citation_node (last_child)) + last_child = webkit_dom_node_get_last_child (last_child); + + /* Before appending any content to the block, check that the + * last node is not BR, if it is, remove it. */ + tmp = webkit_dom_node_get_last_child (last_child); + if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (tmp)) + remove_node (tmp); + + if (in_situ && event->data.fragment) { + webkit_dom_node_append_child ( + webkit_dom_node_get_parent_node (last_child), + webkit_dom_node_clone_node_with_error ( + WEBKIT_DOM_NODE (event->data.fragment), TRUE, NULL), + NULL); + } else { + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (child)); + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (child)); + + e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child)); + e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child)); + + /* Copy the content of the first block to the last block to get + * to the state how the block looked like before it was split. */ + while ((tmp = webkit_dom_node_get_first_child (child))) + webkit_dom_node_append_child (last_child, tmp, NULL); + + e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (last_child)); + + remove_node (child); + } + + /* Move all the block from next citation to the previous one. */ + while ((child = webkit_dom_node_get_first_child (citation_after))) + webkit_dom_node_append_child (citation_before, child, NULL); + + dom_remove_selection_markers (document); + + remove_node (WEBKIT_DOM_NODE (parent)); + remove_node (WEBKIT_DOM_NODE (citation_after)); + + /* If enter was pressed when some text was selected, restore it. */ + if (event->data.fragment != NULL && !in_situ) + undo_delete (editor_page, event); + + e_editor_dom_merge_siblings_if_necessary (editor_page, NULL); + + restore_selection_to_history_event_state (editor_page, event->before); + + e_editor_dom_force_spell_check_in_viewport (editor_page); + } else { + restore_selection_to_history_event_state (editor_page, event->before); + + if (in_situ) { + WebKitDOMElement *selection_start_marker; + WebKitDOMNode *block; + + e_editor_dom_selection_save (editor_page); + + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + block = e_editor_dom_get_parent_block_node_from_child ( + WEBKIT_DOM_NODE (selection_start_marker)); + dom_remove_selection_markers (document); + + /* Remove current block (and all of its parents if they + * are empty) as it will be replaced by a new block that + * will be in the body and not in the blockquote. */ + e_editor_dom_remove_node_and_parents_if_empty (block); + } + + e_editor_dom_insert_new_line_into_citation (editor_page, ""); + } +} + +static void +undo_redo_unquote (EEditorPage *editor_page, + EEditorHistoryEvent *event, + gboolean undo) +{ + WebKitDOMDocument *document; + WebKitDOMElement *element; + + document = e_editor_page_get_document (editor_page); + + restore_selection_to_history_event_state (editor_page, undo ? event->after : event->before); + + e_editor_dom_selection_save (editor_page); + element = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + + if (undo) { + WebKitDOMNode *next_sibling, *prev_sibling; + WebKitDOMElement *block; + + block = get_parent_block_element (WEBKIT_DOM_NODE (element)); + + next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (block)); + prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (block)); + + if (prev_sibling && e_editor_dom_node_is_citation_node (prev_sibling)) { + webkit_dom_node_append_child ( + prev_sibling, + webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, NULL), + NULL); + + if (next_sibling && e_editor_dom_node_is_citation_node (next_sibling)) { + WebKitDOMNode *child; + + while ((child = webkit_dom_node_get_first_child (next_sibling))) + webkit_dom_node_append_child ( + prev_sibling, child, NULL); + + remove_node (next_sibling); + } + } else if (next_sibling && e_editor_dom_node_is_citation_node (next_sibling)) { + webkit_dom_node_insert_before ( + next_sibling, + webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, NULL), + webkit_dom_node_get_first_child (next_sibling), + NULL); + } + + remove_node (WEBKIT_DOM_NODE (block)); + } else + e_editor_dom_move_quoted_block_level_up (editor_page); + + if (undo) + e_editor_dom_selection_restore (editor_page); + else + restore_selection_to_history_event_state (editor_page, event->after); + + e_editor_dom_force_spell_check_for_current_paragraph (editor_page); +} + +gboolean +e_editor_undo_redo_manager_is_operation_in_progress (EEditorUndoRedoManager *manager) +{ + g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), FALSE); + + return manager->priv->operation_in_progress; +} + +void +e_editor_undo_redo_manager_set_operation_in_progress (EEditorUndoRedoManager *manager, + gboolean value) +{ + g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager)); + + manager->priv->operation_in_progress = value; +} + +static void +free_history_event_content (EEditorHistoryEvent *event) +{ + switch (event->type) { + case HISTORY_INPUT: + case HISTORY_DELETE: + case HISTORY_CITATION_SPLIT: + case HISTORY_IMAGE: + case HISTORY_SMILEY: + case HISTORY_REMOVE_LINK: + if (event->data.fragment != NULL) + g_clear_object (&event->data.fragment); + break; + case HISTORY_FONT_COLOR: + case HISTORY_PASTE: + case HISTORY_PASTE_AS_TEXT: + case HISTORY_PASTE_QUOTED: + case HISTORY_INSERT_HTML: + case HISTORY_REPLACE: + case HISTORY_REPLACE_ALL: + if (event->data.string.from != NULL) + g_free (event->data.string.from); + if (event->data.string.to != NULL) + g_free (event->data.string.to); + break; + case HISTORY_HRULE_DIALOG: + case HISTORY_IMAGE_DIALOG: + case HISTORY_CELL_DIALOG: + case HISTORY_TABLE_DIALOG: + case HISTORY_TABLE_INPUT: + case HISTORY_PAGE_DIALOG: + case HISTORY_UNQUOTE: + case HISTORY_LINK_DIALOG: + if (event->data.dom.from != NULL) + g_clear_object (&event->data.dom.from); + if (event->data.dom.to != NULL) + g_clear_object (&event->data.dom.to); + break; + default: + break; + } +} + +static void +free_history_event (EEditorHistoryEvent *event) +{ + if (event == NULL) + return; + + free_history_event_content (event); + + g_free (event); +} + +static void +remove_history_event (EEditorUndoRedoManager *manager, + GList *item) +{ + free_history_event_content (item->data); + + manager->priv->history = g_list_delete_link (manager->priv->history, item); + manager->priv->history_size--; +} + +static void +remove_forward_redo_history_events_if_needed (EEditorUndoRedoManager *manager) +{ + GList *history = manager->priv->history; + GList *item; + + if (!history || !history->prev) + return; + + item = history->prev; + while (item) { + GList *prev_item = item->prev; + + remove_history_event (manager, item); + item = prev_item; + } +} + +void +e_editor_undo_redo_manager_insert_history_event (EEditorUndoRedoManager *manager, + EEditorHistoryEvent *event) +{ + g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager)); + + if (manager->priv->operation_in_progress) + return; + + d (printf ("\nINSERTING EVENT:\n")); + d (print_history_event (event)); + + remove_forward_redo_history_events_if_needed (manager); + + if (manager->priv->history_size >= HISTORY_SIZE_LIMIT) { + remove_history_event (manager, g_list_last (manager->priv->history)->prev); + /* FIXME WK2 - what if g_list_last (manager->priv->history) returns NULL? */ + while (((EEditorHistoryEvent *) (g_list_last (manager->priv->history)->prev))->type == HISTORY_AND) { + remove_history_event (manager, g_list_last (manager->priv->history)->prev); + remove_history_event (manager, g_list_last (manager->priv->history)->prev); + } + + } + + manager->priv->history = g_list_prepend (manager->priv->history, event); + manager->priv->history_size++; + + d (print_history (manager)); + + g_object_notify (G_OBJECT (manager), "can-undo"); +} + +EEditorHistoryEvent * +e_editor_undo_redo_manager_get_current_history_event (EEditorUndoRedoManager *manager) +{ + g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), NULL); + + if (manager->priv->history) + return manager->priv->history->data; + + return NULL; +} + +void +e_editor_undo_redo_manager_remove_current_history_event (EEditorUndoRedoManager *manager) +{ + g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager)); + + if (!manager->priv->history) + return; + + remove_history_event (manager, manager->priv->history); +} + +void +e_editor_undo_redo_manager_insert_dash_history_event (EEditorUndoRedoManager *manager) +{ + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + EEditorPage *editor_page; + EEditorHistoryEvent *event, *last; + GList *history; + + g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager)); + + editor_page = editor_undo_redo_manager_ref_editor_page (manager); + g_return_if_fail (editor_page != NULL); + + event = g_new0 (EEditorHistoryEvent, 1); + event->type = HISTORY_INPUT; + + document = e_editor_page_get_document (editor_page); + fragment = webkit_dom_document_create_document_fragment (document); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + webkit_dom_document_create_text_node (document, "-")), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, TRUE)), + NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (fragment), + WEBKIT_DOM_NODE ( + dom_create_selection_marker (document, FALSE)), + NULL); + event->data.fragment = fragment; + + last = e_editor_undo_redo_manager_get_current_history_event (manager); + /* The dash event needs to have the same coordinates as the character + * that is right after it. */ + event->after.start.x = last->after.start.x; + event->after.start.y = last->after.start.y; + event->after.end.x = last->after.end.x; + event->after.end.y = last->after.end.y; + + history = manager->priv->history; + if (history) { + EEditorHistoryEvent *item; + WebKitDOMNode *first_child; + + item = history->data; + + if (item->type != HISTORY_INPUT) { + g_object_unref (editor_page); + return; + } + + first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (item->data.fragment)); + if (WEBKIT_DOM_IS_TEXT (first_child)) { + guint diff; + + diff = event->after.start.x - item->after.start.x; + + /* We need to move the coordinate of the last + * event by one character. */ + last->after.start.x += diff; + last->after.end.x += diff; + + manager->priv->history = g_list_insert_before ( + manager->priv->history, history, event); + } + } + + g_object_unref (editor_page); +} + +gboolean +e_editor_undo_redo_manager_can_undo (EEditorUndoRedoManager *manager) +{ + g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), FALSE); + + if (manager->priv->history) { + EEditorHistoryEvent *event; + + event = manager->priv->history->data; + + return (event->type != HISTORY_START); + } else + return FALSE; +} + +void +e_editor_undo_redo_manager_undo (EEditorUndoRedoManager *manager) +{ + EEditorHistoryEvent *event; + EEditorPage *editor_page; + GList *history; + + g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager)); + + if (!e_editor_undo_redo_manager_can_undo (manager)) + return; + + history = manager->priv->history; + event = history->data; + + d (printf ("\nUNDOING EVENT:\n")); + d (print_history_event (event)); + + manager->priv->operation_in_progress = TRUE; + + editor_page = editor_undo_redo_manager_ref_editor_page (manager); + g_return_if_fail (editor_page != NULL); + + switch (event->type) { + case HISTORY_BOLD: + case HISTORY_ITALIC: + case HISTORY_STRIKETHROUGH: + case HISTORY_UNDERLINE: + case HISTORY_FONT_SIZE: + if (event_selection_was_collapsed (event)) { + if (history->next) { + manager->priv->history = history->next; + e_editor_undo_redo_manager_undo (manager); + } + manager->priv->operation_in_progress = FALSE; + g_object_unref (editor_page); + return; + } + case HISTORY_ALIGNMENT: + case HISTORY_BLOCK_FORMAT: + case HISTORY_MONOSPACE: + undo_redo_style_change (editor_page, event, TRUE); + break; + case HISTORY_DELETE: + undo_delete (editor_page, event); + break; + case HISTORY_INDENT: + undo_redo_indent (editor_page, event, TRUE); + break; + case HISTORY_INPUT: + undo_input (manager, editor_page, event); + break; + case HISTORY_REMOVE_LINK: + undo_redo_remove_link (editor_page, event, TRUE); + break; + case HISTORY_FONT_COLOR: + undo_redo_font_color (editor_page, event, TRUE); + break; + case HISTORY_CITATION_SPLIT: + undo_redo_citation_split (editor_page, event, TRUE); + break; + case HISTORY_PASTE: + case HISTORY_PASTE_AS_TEXT: + case HISTORY_PASTE_QUOTED: + case HISTORY_INSERT_HTML: + undo_redo_paste (editor_page, event, TRUE); + break; + case HISTORY_IMAGE: + case HISTORY_SMILEY: + undo_redo_image (editor_page, event, TRUE); + break; + case HISTORY_WRAP: + undo_redo_wrap (editor_page, event, TRUE); + break; + case HISTORY_IMAGE_DIALOG: + undo_redo_image_dialog (editor_page, event, TRUE); + break; + case HISTORY_LINK_DIALOG: + undo_redo_link_dialog (editor_page, event, TRUE); + break; + case HISTORY_TABLE_DIALOG: + undo_redo_table_dialog (editor_page, event, TRUE); + break; + case HISTORY_TABLE_INPUT: + undo_redo_table_input (editor_page, event, TRUE); + break; + case HISTORY_PAGE_DIALOG: + undo_redo_page_dialog (editor_page, event, TRUE); + break; + case HISTORY_HRULE_DIALOG: + undo_redo_hrule_dialog (editor_page, event, TRUE); + break; + case HISTORY_REPLACE: + case HISTORY_REPLACE_ALL: + undo_redo_replace_all (manager, editor_page, event, TRUE); + break; + case HISTORY_UNQUOTE: + undo_redo_unquote (editor_page, event, TRUE); + break; + case HISTORY_AND: + g_warning ("Unhandled HISTORY_AND event!"); + break; + default: + g_object_unref (editor_page); + return; + } + + /* FIXME WK2 - history->next can be NULL! */ + event = history->next->data; + if (event->type == HISTORY_AND) { + manager->priv->history = history->next->next; + e_editor_undo_redo_manager_undo (manager); + g_object_unref (editor_page); + return; + } + + if (history->next) + manager->priv->history = manager->priv->history->next; + + d (print_undo_events (manager)); +/* FIXME WK2 + html_editor_view_user_changed_contents_cb (view);*/ + + manager->priv->operation_in_progress = FALSE; + + g_object_unref (editor_page); + + g_object_notify (G_OBJECT (manager), "can-undo"); + g_object_notify (G_OBJECT (manager), "can-redo"); +} + +gboolean +e_editor_undo_redo_manager_can_redo (EEditorUndoRedoManager *manager) +{ + g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), FALSE); + + if (manager->priv->history && manager->priv->history->prev) + return TRUE; + else + return FALSE; +} + +void +e_editor_undo_redo_manager_redo (EEditorUndoRedoManager *manager) +{ + EEditorPage *editor_page; + EEditorHistoryEvent *event; + GList *history; + + if (!e_editor_undo_redo_manager_can_redo (manager)) + return; + + history = manager->priv->history; + event = history->prev->data; + + d (printf ("\nREDOING EVENT:\n")); + d (print_history_event (event)); + + editor_page = editor_undo_redo_manager_ref_editor_page (manager); + g_return_if_fail (editor_page != NULL); + + manager->priv->operation_in_progress = TRUE; + + switch (event->type) { + case HISTORY_BOLD: + case HISTORY_MONOSPACE: + case HISTORY_STRIKETHROUGH: + case HISTORY_UNDERLINE: + case HISTORY_ALIGNMENT: + case HISTORY_BLOCK_FORMAT: + case HISTORY_FONT_SIZE: + case HISTORY_ITALIC: + undo_redo_style_change (editor_page, event, FALSE); + break; + case HISTORY_DELETE: + redo_delete (editor_page, event); + break; + case HISTORY_INDENT: + undo_redo_indent (editor_page, event, FALSE); + break; + case HISTORY_INPUT: + undo_delete (editor_page, event); + e_editor_dom_check_magic_smileys (editor_page); + { + gchar *text_content; + WebKitDOMNode *first_child; + + first_child = webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (event->data.fragment)); + text_content = webkit_dom_node_get_text_content (first_child); + /* Call magic links when the space was pressed. */ + if (g_str_has_prefix (text_content, UNICODE_NBSP)) { + e_editor_page_set_space_key_pressed (editor_page, TRUE); + e_editor_dom_check_magic_links (editor_page, FALSE); + e_editor_page_set_space_key_pressed (editor_page, FALSE); + } + g_free (text_content); + } + break; + case HISTORY_REMOVE_LINK: + undo_redo_remove_link (editor_page, event, FALSE); + break; + case HISTORY_FONT_COLOR: + undo_redo_font_color (editor_page, event, FALSE); + break; + case HISTORY_CITATION_SPLIT: + undo_redo_citation_split (editor_page, event, FALSE); + break; + case HISTORY_PASTE: + case HISTORY_PASTE_AS_TEXT: + case HISTORY_PASTE_QUOTED: + case HISTORY_INSERT_HTML: + undo_redo_paste (editor_page, event, FALSE); + break; + case HISTORY_IMAGE: + case HISTORY_SMILEY: + undo_redo_image (editor_page, event, FALSE); + break; + case HISTORY_WRAP: + undo_redo_wrap (editor_page, event, FALSE); + break; + case HISTORY_IMAGE_DIALOG: + undo_redo_image_dialog (editor_page, event, FALSE); + break; + case HISTORY_LINK_DIALOG: + undo_redo_link_dialog (editor_page, event, FALSE); + break; + case HISTORY_TABLE_DIALOG: + undo_redo_table_dialog (editor_page, event, FALSE); + break; + case HISTORY_TABLE_INPUT: + undo_redo_table_input (editor_page, event, FALSE); + break; + case HISTORY_PAGE_DIALOG: + undo_redo_page_dialog (editor_page, event, FALSE); + break; + case HISTORY_HRULE_DIALOG: + undo_redo_hrule_dialog (editor_page, event, FALSE); + break; + case HISTORY_REPLACE: + case HISTORY_REPLACE_ALL: + undo_redo_replace_all (manager, editor_page, event, FALSE); + break; + case HISTORY_UNQUOTE: + undo_redo_unquote (editor_page, event, FALSE); + break; + case HISTORY_AND: + g_warning ("Unhandled HISTORY_AND event!"); + break; + default: + g_object_unref (editor_page); + return; + } + + /* FIXME WK2 - what if history->prev is NULL? */ + if (history->prev->prev) { + event = history->prev->prev->data; + if (event->type == HISTORY_AND) { + manager->priv->history = manager->priv->history->prev->prev; + e_editor_undo_redo_manager_redo (manager); + g_object_unref (editor_page); + return; + } + } + + manager->priv->history = manager->priv->history->prev; + + d (print_redo_events (manager)); +/* FIXME WK2 + html_editor_view_user_changed_contents_cb (view);*/ + + manager->priv->operation_in_progress = FALSE; + + g_object_unref (editor_page); + + g_object_notify (G_OBJECT (manager), "can-undo"); + g_object_notify (G_OBJECT (manager), "can-redo"); +} + +void +e_editor_undo_redo_manager_clean_history (EEditorUndoRedoManager *manager) +{ + EEditorPage *editor_page; + EEditorHistoryEvent *ev; + + g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager)); + + if (manager->priv->history != NULL) { + g_list_free_full (manager->priv->history, (GDestroyNotify) free_history_event); + manager->priv->history = NULL; + } + + manager->priv->history_size = 0; + editor_page = editor_undo_redo_manager_ref_editor_page (manager); + g_return_if_fail (editor_page != NULL); + e_editor_page_set_dont_save_history_in_body_input (editor_page, FALSE); + g_object_unref (editor_page); + manager->priv->operation_in_progress = FALSE; + + ev = g_new0 (EEditorHistoryEvent, 1); + ev->type = HISTORY_START; + manager->priv->history = g_list_append (manager->priv->history, ev); + + g_object_notify (G_OBJECT (manager), "can-undo"); + g_object_notify (G_OBJECT (manager), "can-redo"); +} + +static void +editor_undo_redo_manager_set_editor_page (EEditorUndoRedoManager *manager, + EEditorPage *editor_page) +{ + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + g_weak_ref_set (&manager->priv->editor_page, editor_page); +} + +static void +editor_undo_redo_manager_dispose (GObject *object) +{ + EEditorUndoRedoManagerPrivate *priv; + + priv = E_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE (object); + + if (priv->history != NULL) { + g_list_free_full (priv->history, (GDestroyNotify) free_history_event); + priv->history = NULL; + } + + g_weak_ref_set (&priv->editor_page, NULL); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_editor_undo_redo_manager_parent_class)->dispose (object); +} + +static void +editor_undo_redo_manager_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CAN_REDO: + g_value_set_boolean ( + value, e_editor_undo_redo_manager_can_redo ( + E_EDITOR_UNDO_REDO_MANAGER (object))); + return; + + case PROP_CAN_UNDO: + g_value_set_boolean ( + value, e_editor_undo_redo_manager_can_undo ( + E_EDITOR_UNDO_REDO_MANAGER (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +editor_undo_redo_manager_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_EDITOR_PAGE: + editor_undo_redo_manager_set_editor_page ( + E_EDITOR_UNDO_REDO_MANAGER (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_editor_undo_redo_manager_class_init (EEditorUndoRedoManagerClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EEditorUndoRedoManagerPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = editor_undo_redo_manager_dispose; + object_class->get_property = editor_undo_redo_manager_get_property; + object_class->set_property = editor_undo_redo_manager_set_property; + + /** + * EEditorUndoRedoManager:can-redo + * + * Determines whether it's possible to redo previous action. The action + * is usually disabled when there is no action to redo. + */ + g_object_class_install_property ( + object_class, + PROP_CAN_REDO, + g_param_spec_boolean ( + "can-redo", + "Can Redo", + NULL, + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * EEditorUndoRedoManager:can-undo + * + * Determines whether it's possible to undo last action. The action + * is usually disabled when there is no previous action to undo. + */ + g_object_class_install_property ( + object_class, + PROP_CAN_UNDO, + g_param_spec_boolean ( + "can-undo", + "Can Undo", + NULL, + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_EDITOR_PAGE, + g_param_spec_object ( + "editor-page", + NULL, + NULL, + E_TYPE_EDITOR_PAGE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_editor_undo_redo_manager_init (EEditorUndoRedoManager *manager) +{ + manager->priv = E_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE (manager); + + manager->priv->operation_in_progress = FALSE; + manager->priv->history = NULL; + manager->priv->history_size = 0; +} diff --git a/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h b/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h new file mode 100644 index 0000000..60dabaf --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h @@ -0,0 +1,175 @@ +/* + * e-editor-undo-redo-manager.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_EDITOR_UNDO_REDO_MANAGER_H +#define E_EDITOR_UNDO_REDO_MANAGER_H + +#include <glib-object.h> +#include <webkitdom/webkitdom.h> + +#define E_TYPE_EDITOR_UNDO_REDO_MANAGER \ + (e_editor_undo_redo_manager_get_type ()) +#define E_EDITOR_UNDO_REDO_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_EDITOR_UNDO_REDO_MANAGER, EEditorUndoRedoManager)) +#define E_EDITOR_UNDO_REDO_MANAGER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_EDITOR_UNDO_REDO_MANAGER, EEditorUndoRedoManagerClass)) +#define E_IS_EDITOR_UNDO_REDO_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_EDITOR_UNDO_REDO_MANAGER)) +#define E_IS_EDITOR_UNDO_REDO_MANAGER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_EDITOR_UNDO_REDO_MANAGER)) +#define E_EDITOR_UNDO_REDO_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_EDITOR_UNDO_REDO_MANAGER, EEditorUndoRedoManagerClass)) + +G_BEGIN_DECLS + +struct _EEditorPage; + +enum EEditorHistoryEventType { + HISTORY_ALIGNMENT, + HISTORY_AND, + HISTORY_BLOCK_FORMAT, + HISTORY_BOLD, + HISTORY_CELL_DIALOG, + HISTORY_DELETE, /* BackSpace, Delete, with and without selection */ + HISTORY_FONT_COLOR, + HISTORY_FONT_SIZE, + HISTORY_HRULE_DIALOG, + HISTORY_INDENT, + HISTORY_INPUT, + HISTORY_IMAGE, + HISTORY_IMAGE_DIALOG, + HISTORY_INSERT_HTML, + HISTORY_ITALIC, + HISTORY_LINK_DIALOG, + HISTORY_MONOSPACE, + HISTORY_PAGE_DIALOG, + HISTORY_PASTE, + HISTORY_PASTE_AS_TEXT, + HISTORY_PASTE_QUOTED, + HISTORY_REMOVE_LINK, + HISTORY_REPLACE, + HISTORY_REPLACE_ALL, + HISTORY_CITATION_SPLIT, + HISTORY_SMILEY, + HISTORY_START, /* Start of history */ + HISTORY_STRIKETHROUGH, + HISTORY_TABLE_DIALOG, + HISTORY_TABLE_INPUT, + HISTORY_UNDERLINE, + HISTORY_WRAP, + HISTORY_UNQUOTE +}; + +typedef struct { + gint from; /* From what format we are changing. */ + gint to; /* To what format we are changing. */ +} EEditorStyleChange; + +/* This is used for e-html-editor-*-dialogs */ +typedef struct { + WebKitDOMNode *from; /* From what node we are changing. */ + WebKitDOMNode *to; /* To what node we are changing. */ +} EEditorDOMChange; + +typedef struct { + gchar *from; /* From what format we are changing. */ + gchar *to; /* To what format we are changing. */ +} EEditorStringChange; + +typedef struct { + guint x; + guint y; +} EEditorSelectionPoint; + +typedef struct { + EEditorSelectionPoint start; + EEditorSelectionPoint end; +} EEditorSelection; + +typedef struct { + enum EEditorHistoryEventType type; + EEditorSelection before; + EEditorSelection after; + union { + WebKitDOMDocumentFragment *fragment; + EEditorStyleChange style; + EEditorStringChange string; + EEditorDOMChange dom; + } data; +} EEditorHistoryEvent; + +typedef struct _EEditorUndoRedoManager EEditorUndoRedoManager; +typedef struct _EEditorUndoRedoManagerClass EEditorUndoRedoManagerClass; +typedef struct _EEditorUndoRedoManagerPrivate EEditorUndoRedoManagerPrivate; + +struct _EEditorUndoRedoManager { + GObject parent; + EEditorUndoRedoManagerPrivate *priv; +}; + +struct _EEditorUndoRedoManagerClass +{ + GObjectClass parent_class; +}; + +GType e_editor_undo_redo_manager_get_type + (void) G_GNUC_CONST; + +EEditorUndoRedoManager * + e_editor_undo_redo_manager_new (struct _EEditorPage *editor_page); +gboolean e_editor_undo_redo_manager_is_operation_in_progress + (EEditorUndoRedoManager *manager); + +void e_editor_undo_redo_manager_set_operation_in_progress + (EEditorUndoRedoManager *manager, + gboolean value); + +void e_editor_undo_redo_manager_insert_history_event + (EEditorUndoRedoManager *manager, + EEditorHistoryEvent *event); + +EEditorHistoryEvent * + e_editor_undo_redo_manager_get_current_history_event + (EEditorUndoRedoManager *manager); +void e_editor_undo_redo_manager_remove_current_history_event + (EEditorUndoRedoManager *manager); + +void e_editor_undo_redo_manager_insert_dash_history_event + (EEditorUndoRedoManager *manager); + +gboolean e_editor_undo_redo_manager_can_undo + (EEditorUndoRedoManager *manager); + +void e_editor_undo_redo_manager_undo (EEditorUndoRedoManager *manager); + +gboolean e_editor_undo_redo_manager_can_redo + (EEditorUndoRedoManager *manager); + +void e_editor_undo_redo_manager_redo (EEditorUndoRedoManager *manager); + +void e_editor_undo_redo_manager_clean_history + (EEditorUndoRedoManager *manager); + +G_END_DECLS + +#endif /* E_EDITOR_UNDO_REDO_MANAGER_H */ diff --git a/modules/webkit-editor/web-extension/e-editor-web-extension-main.c b/modules/webkit-editor/web-extension/e-editor-web-extension-main.c new file mode 100644 index 0000000..244bc4c --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-web-extension-main.c @@ -0,0 +1,57 @@ +/* + * e-html-editor-web-extension-main.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <camel/camel.h> + +#include "e-editor-web-extension.h" + +static void +bus_acquired_cb (GDBusConnection *connection, + const gchar *name, + EEditorWebExtension *extension) +{ + e_editor_web_extension_dbus_register (extension, connection); +} + +/* Forward declaration */ +G_MODULE_EXPORT void webkit_web_extension_initialize (WebKitWebExtension *wk_extension); + +G_MODULE_EXPORT void +webkit_web_extension_initialize (WebKitWebExtension *wk_extension) +{ + EEditorWebExtension *extension; + + camel_debug_init (); + + extension = e_editor_web_extension_get_default (); + e_editor_web_extension_initialize (extension, wk_extension); + + g_bus_own_name ( + G_BUS_TYPE_SESSION, + E_WEBKIT_EDITOR_WEB_EXTENSION_SERVICE_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + (GBusAcquiredCallback) bus_acquired_cb, + NULL, /* GBusNameAcquiredCallback */ + NULL, /* GBusNameLostCallback */ + g_object_ref (extension), + (GDestroyNotify) g_object_unref); +} diff --git a/modules/webkit-editor/web-extension/e-editor-web-extension-names.h b/modules/webkit-editor/web-extension/e-editor-web-extension-names.h new file mode 100644 index 0000000..d1b5f02 --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-web-extension-names.h @@ -0,0 +1,26 @@ +/* + * e-editor-web-extension-names.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_EDITOR_WEB_EXTENSION_NAMES_H +#define E_EDITOR_WEB_EXTENSION_NAMES_H + +#define E_WEBKIT_EDITOR_WEB_EXTENSION_SERVICE_NAME "org.gnome.Evolution.WebExtension.EWebKitEditor" +#define E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH "/org/gnome/Evolution/WebExtension/EWebKitEditor" +#define E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE "org.gnome.Evolution.WebExtension.EWebKitEditor" + +#endif /* E_EDITOR_WEB_EXTENSION_NAMES_H */ diff --git a/modules/webkit-editor/web-extension/e-editor-web-extension.c b/modules/webkit-editor/web-extension/e-editor-web-extension.c new file mode 100644 index 0000000..f387e26 --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-web-extension.c @@ -0,0 +1,2501 @@ +/* + * e-editor-web-extension.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gstdio.h> +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <webkit2/webkit-web-extension.h> +#include <camel/camel.h> + +#include "web-extensions/e-dom-utils.h" + +#include "e-editor-page.h" +#include "e-composer-dom-functions.h" +#include "e-dialogs-dom-functions.h" +#include "e-editor-dom-functions.h" +#include "e-editor-undo-redo-manager.h" + +#include "e-editor-web-extension.h" + +#define E_EDITOR_WEB_EXTENSION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_EDITOR_WEB_EXTENSION, EEditorWebExtensionPrivate)) + +struct _EEditorWebExtensionPrivate { + WebKitWebExtension *wk_extension; + + GDBusConnection *dbus_connection; + guint registration_id; + + GHashTable *editor_pages; /* guint64 *webpage_id ~> EEditorPage * */ +}; + +static CamelDataCache *emd_global_http_cache = NULL; + +static const gchar *introspection_xml = +"<node>" +" <interface name='" E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE "'>" +"<!-- ********************************************************* -->" +"<!-- SIGNALS -->" +"<!-- ********************************************************* -->" +" <signal name='SelectionChanged'>" +" <arg type='t' name='page_id' direction='out'/>" +" <arg type='i' name='alignment' direction='out'/>" +" <arg type='i' name='block_format' direction='out'/>" +" <arg type='b' name='indented' direction='out'/>" +" <arg type='i' name='style_flags' direction='out'/>" +" <arg type='i' name='font_size' direction='out'/>" +" <arg type='s' name='font_color' direction='out'/>" +" </signal>" +" <signal name='ContentChanged'>" +" <arg type='t' name='page_id' direction='out'/>" +" </signal>" +" <signal name='UndoRedoStateChanged'>" +" <arg type='t' name='page_id' direction='out'/>" +" <arg type='b' name='can_undo' direction='out'/>" +" <arg type='b' name='can_redo' direction='out'/>" +" </signal>" +"<!-- ********************************************************* -->" +"<!-- METHODS -->" +"<!-- ********************************************************* -->" +"<!-- ********************************************************* -->" +"<!-- FOR TESTING ONLY -->" +"<!-- ********************************************************* -->" +" <method name='TestHTMLEqual'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='html1' direction='in'/>" +" <arg type='s' name='html2' direction='in'/>" +" <arg type='b' name='equal' direction='out'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- GENERIC -->" +"<!-- ********************************************************* -->" +" <method name='ElementHasAttribute'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='attribute' direction='in'/>" +" <arg type='b' name='has_attribute' direction='out'/>" +" </method>" +" <method name='ElementGetAttribute'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='attribute' direction='in'/>" +" <arg type='s' name='value' direction='out'/>" +" </method>" +" <method name='ElementGetAttributeBySelector'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='selector' direction='in'/>" +" <arg type='s' name='attribute' direction='in'/>" +" <arg type='s' name='value' direction='out'/>" +" </method>" +" <method name='ElementRemoveAttribute'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='attribute' direction='in'/>" +" </method>" +" <method name='ElementRemoveAttributeBySelector'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='selector' direction='in'/>" +" <arg type='s' name='attribute' direction='in'/>" +" </method>" +" <method name='ElementSetAttribute'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='attribute' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" </method>" +" <method name='ElementSetAttributeBySelector'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='selector' direction='in'/>" +" <arg type='s' name='attribute' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" </method>" +" <method name='ElementGetTagName'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='tag_name' direction='out'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are specific to composer -->" +"<!-- ********************************************************* -->" +" <method name='RemoveImageAttributesFromElementBySelector'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='selector' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorCellDialog -->" +"<!-- ********************************************************* -->" +" <method name='EEditorCellDialogMarkCurrentCellElement'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSaveHistoryOnExit'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSetElementVAlign'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" <arg type='i' name='scope' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSetElementAlign'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" <arg type='i' name='scope' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSetElementNoWrap'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='value' direction='in'/>" +" <arg type='i' name='scope' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSetElementHeaderStyle'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='value' direction='in'/>" +" <arg type='i' name='scope' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSetElementWidth'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" <arg type='i' name='scope' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSetElementColSpan'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='i' name='value' direction='in'/>" +" <arg type='i' name='scope' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSetElementRowSpan'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='i' name='value' direction='in'/>" +" <arg type='i' name='scope' direction='in'/>" +" </method>" +" <method name='EEditorCellDialogSetElementBgColor'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" <arg type='i' name='scope' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorHRuleDialog -->" +"<!-- ********************************************************* -->" +" <method name='EEditorHRuleDialogFindHRule'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='created_new_hr' direction='out'/>" +" </method>" +" <method name='EEditorHRuleDialogOnClose'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorImageDialog -->" +"<!-- ********************************************************* -->" +" <method name='EEditorImageDialogMarkImage'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorImageDialogSaveHistoryOnExit'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorImageDialogSetElementUrl'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" </method>" +" <method name='EEditorImageDialogGetElementUrl'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='value' direction='out'/>" +" </method>" +" <method name='ImageElementSetWidth'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='in'/>" +" </method>" +" <method name='ImageElementGetWidth'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='out'/>" +" </method>" +" <method name='ImageElementSetHeight'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='in'/>" +" </method>" +" <method name='ImageElementGetHeight'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='out'/>" +" </method>" +" <method name='ImageElementGetNaturalWidth'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='out'/>" +" </method>" +" <method name='ImageElementGetNaturalHeight'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='out'/>" +" </method>" +" <method name='ImageElementSetHSpace'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='in'/>" +" </method>" +" <method name='ImageElementGetHSpace'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='out'/>" +" </method>" +" <method name='ImageElementSetVSpace'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='in'/>" +" </method>" +" <method name='ImageElementGetVSpace'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='value' direction='out'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorLinkDialog -->" +"<!-- ********************************************************* -->" +" <method name='EEditorLinkDialogOk'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='url' direction='in'/>" +" <arg type='s' name='inner_text' direction='in'/>" +" </method>" +" <method name='EEditorLinkDialogShow'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='url' direction='out'/>" +" <arg type='s' name='inner_text' direction='out'/>" +" </method>" +" <method name='EEditorLinkDialogOnOpen'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorLinkDialogOnClose'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorLinkDialogUnlink'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorPageDialog -->" +"<!-- ********************************************************* -->" +" <method name='EEditorPageDialogSaveHistory'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorPageDialogSaveHistoryOnExit'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorSpellCheckDialog -->" +"<!-- ********************************************************* -->" +" <method name='EEditorSpellCheckDialogNext'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='word' direction='in'/>" +" <arg type='as' name='languages' direction='in'/>" +" <arg type='s' name='next_word' direction='out'/>" +" </method>" +" <method name='EEditorSpellCheckDialogPrev'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='word' direction='in'/>" +" <arg type='as' name='languages' direction='in'/>" +" <arg type='s' name='prev_word' direction='out'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorTableDialog -->" +"<!-- ********************************************************* -->" +" <method name='EEditorTableDialogSetRowCount'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='u' name='value' direction='in'/>" +" </method>" +" <method name='EEditorTableDialogGetRowCount'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='u' name='value' direction='out'/>" +" </method>" +" <method name='EEditorTableDialogSetColumnCount'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='u' name='value' direction='in'/>" +" </method>" +" <method name='EEditorTableDialogGetColumnCount'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='u' name='value' direction='out'/>" +" </method>" +" <method name='EEditorTableDialogShow'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='created_new_table' direction='out'/>" +" </method>" +" <method name='EEditorTableDialogSaveHistoryOnExit'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorActions -->" +"<!-- ********************************************************* -->" +" <method name='TableCellElementGetNoWrap'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='b' name='no_wrap' direction='out'/>" +" </method>" +" <method name='TableCellElementGetRowSpan'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='row_span' direction='out'/>" +" </method>" +" <method name='TableCellElementGetColSpan'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='i' name='col_span' direction='out'/>" +" </method>" +" <method name='EEditorDialogDeleteCellContents'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorDialogDeleteColumn'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorDialogDeleteRow'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorDialogDeleteTable'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorDialogInsertColumnAfter'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorDialogInsertColumnBefore'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorDialogInsertRowAbove'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorDialogInsertRowBelow'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EEditorActionsSaveHistoryForCut'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorView -->" +"<!-- ********************************************************* -->" +" <method name='SetPastingContentFromItself'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='value' direction='in'/>" +" </method>" +" <method name='SetEditorHTMLMode'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='html_mode' direction='in'/>" +" <arg type='b' name='convert' direction='in'/>" +" </method>" +" <method name='SetConvertInSitu'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='value' direction='in'/>" +" </method>" +" <method name='DOMForceSpellCheck'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMTurnSpellCheckOff'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMScrollToCaret'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMEmbedStyleSheet'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='style_sheet_content' direction='in'/>" +" </method>" +" <method name='DOMRemoveEmbeddedStyleSheet'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMSaveSelection'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMRestoreSelection'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMUndo'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMRedo'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMQuoteAndInsertTextIntoSelection'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='text' direction='in'/>" +" <arg type='b' name='is_html' direction='in'/>" +" </method>" +" <method name='DOMConvertAndInsertHTMLIntoSelection'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='text' direction='in'/>" +" <arg type='b' name='is_html' direction='in'/>" +" </method>" +" <method name='DOMCheckIfConversionNeeded'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='conversion_needed' direction='out'/>" +" </method>" +" <method name='DOMGetContent'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='from_domain' direction='in'/>" +" <arg type='i' name='flags' direction='in'/>" +" <arg type='s' name='content' direction='out'/>" +" <arg type='v' name='inline_images' direction='out'/>" +" </method>" +" <method name='DOMInsertHTML'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='html' direction='in'/>" +" </method>" +" <method name='DOMConvertContent'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='preffered_text' direction='in'/>" +" </method>" +" <method name='DOMAddNewInlineImageIntoList'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='filename' direction='in'/>" +" <arg type='s' name='cid_src' direction='in'/>" +" <arg type='s' name='src' direction='in'/>" +" </method>" +" <method name='DOMReplaceImageSrc'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='selector' direction='in'/>" +" <arg type='s' name='uri' direction='in'/>" +" </method>" +" <method name='DOMDragAndDropEnd'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMMoveSelectionOnPoint'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='i' name='x' direction='in'/>" +" <arg type='i' name='y' direction='in'/>" +" <arg type='b' name='cancel_if_not_collapsed' direction='in'/>" +" </method>" +" <method name='DOMInsertSmiley'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='smiley_name' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EEditorSelection -->" +"<!-- ********************************************************* -->" +" <method name='DOMSelectionIndent'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMSelectionInsertImage'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='uri' direction='in'/>" +" </method>" +" <method name='DOMSelectionReplace'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='replacement' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetAlignment'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='i' name='alignment' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetBold'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='bold' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetBlockFormat'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='i' name='block_format' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetFontColor'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='color' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetFontSize'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='i' name='font_size' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetItalic'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='italic' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetMonospaced'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='monospaced' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetStrikethrough'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='strikethrough' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetSubscript'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='subscript' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetSuperscript'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='superscript' direction='in'/>" +" </method>" +" <method name='DOMSelectionSetUnderline'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='underline' direction='in'/>" +" </method>" +" <method name='DOMSelectionUnindent'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMSelectionWrap'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMGetCaretWord'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='word' direction='out'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in EComposerPrivate -->" +"<!-- ********************************************************* -->" +" <method name='DOMInsertSignature'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='content' direction='in'/>" +" <arg type='b' name='is_html' direction='in'/>" +" <arg type='s' name='signature_id' direction='in'/>" +" <arg type='b' name='set_signature_from_message' direction='in'/>" +" <arg type='b' name='check_if_signature_is_changed' direction='in'/>" +" <arg type='b' name='ignore_next_signature_change' direction='in'/>" +" <arg type='s' name='new_signature_id' direction='out'/>" +" <arg type='b' name='out_set_signature_from_message' direction='out'/>" +" <arg type='b' name='out_check_if_signature_is_changed' direction='out'/>" +" <arg type='b' name='out_ignore_next_signature_change' direction='out'/>" +" </method>" +" <method name='DOMSaveDragAndDropHistory'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='DOMCleanAfterDragAndDrop'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +"<!-- ********************************************************* -->" +"<!-- Functions that are used in External Editor plugin -->" +"<!-- ********************************************************* -->" +" <method name='DOMGetCaretPosition'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='u' name='position' direction='out'/>" +" </method>" +" <method name='DOMGetCaretOffset'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='u' name='offset' direction='out'/>" +" </method>" +" <method name='DOMClearUndoRedoHistory'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" </interface>" +"</node>"; + +G_DEFINE_TYPE (EEditorWebExtension, e_editor_web_extension, G_TYPE_OBJECT) + +static EEditorPage * +get_editor_page (EEditorWebExtension *extension, + guint64 page_id) +{ + g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL); + + return g_hash_table_lookup (extension->priv->editor_pages, &page_id); +} + +static EEditorPage * +get_editor_page_or_return_dbus_error (GDBusMethodInvocation *invocation, + EEditorWebExtension *extension, + guint64 page_id) +{ + WebKitWebPage *web_page; + EEditorPage *editor_page; + + g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL); + + web_page = webkit_web_extension_get_page (extension->priv->wk_extension, page_id); + if (!web_page) { + g_dbus_method_invocation_return_error ( + invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Invalid page ID: %" G_GUINT64_FORMAT, page_id); + + return NULL; + } + + editor_page = get_editor_page (extension, page_id); + if (!editor_page) { + g_dbus_method_invocation_return_error ( + invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Invalid page ID: %" G_GUINT64_FORMAT, page_id); + } + + return editor_page; +} + +static void +handle_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + guint64 page_id; + EEditorWebExtension *extension = E_EDITOR_WEB_EXTENSION (user_data); + WebKitDOMDocument *document; + EEditorPage *editor_page; + + if (g_strcmp0 (interface_name, E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE) != 0) + return; + + if (g_strcmp0 (method_name, "TestHTMLEqual") == 0) { + gboolean equal = FALSE; + const gchar *html1 = NULL, *html2 = NULL; + + g_variant_get (parameters, "(t&s&s)", &page_id, &html1, &html2); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + equal = e_editor_dom_test_html_equal (document, html1, html2); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", equal)); + } else if (g_strcmp0 (method_name, "ElementHasAttribute") == 0) { + gboolean value = FALSE; + const gchar *element_id, *attribute; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s&s)", &page_id, &element_id, &attribute); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_element_has_attribute (element, attribute); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(b)", value)); + } else if (g_strcmp0 (method_name, "ElementGetAttribute") == 0) { + const gchar *element_id, *attribute; + gchar *value = NULL; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s&s)", &page_id, &element_id, &attribute); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_element_get_attribute (element, attribute); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + value ? value : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "ElementGetAttributeBySelector") == 0) { + const gchar *attribute, *selector; + gchar *value = NULL; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s&s)", &page_id, &selector, &attribute); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_query_selector (document, selector, NULL); + if (element) + value = webkit_dom_element_get_attribute (element, attribute); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + value ? value : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "ElementRemoveAttribute") == 0) { + const gchar *element_id, *attribute; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s&s)", &page_id, &element_id, &attribute); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + webkit_dom_element_remove_attribute (element, attribute); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ElementRemoveAttributeBySelector") == 0) { + const gchar *attribute, *selector; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s&s)", &page_id, &selector, &attribute); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_query_selector (document, selector, NULL); + if (element) + webkit_dom_element_remove_attribute (element, attribute); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ElementSetAttribute") == 0) { + const gchar *element_id, *attribute, *value; + WebKitDOMElement *element; + + g_variant_get ( + parameters, + "(t&s&s&s)", + &page_id, &element_id, &attribute, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + webkit_dom_element_set_attribute ( + element, attribute, value, NULL); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ElementSetAttributeBySelector") == 0) { + const gchar *attribute, *selector, *value; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s&s&s)", &page_id, &selector, &attribute, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_query_selector (document, selector, NULL); + if (element) { + if (g_strcmp0 (selector, "body") == 0 && + g_strcmp0 (attribute, "link") == 0) + e_editor_dom_set_link_color (editor_page, value); + else if (g_strcmp0 (selector, "body") == 0 && + g_strcmp0 (attribute, "vlink") == 0) + e_editor_dom_set_visited_link_color (editor_page, value); + else + webkit_dom_element_set_attribute ( + element, attribute, value, NULL); + } + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ElementGetTagName") == 0) { + const gchar *element_id; + gchar *value = NULL; + WebKitDOMElement *element; + + g_variant_get (parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_element_get_tag_name (element); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + value ? value : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "RemoveImageAttributesFromElementBySelector") == 0) { + const gchar *selector; + WebKitDOMElement *element; + + g_variant_get (parameters, "(t&s)", &page_id, &selector); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_query_selector (document, selector, NULL); + if (element) { + webkit_dom_element_remove_attribute (element, "background"); + webkit_dom_element_remove_attribute (element, "data-uri"); + webkit_dom_element_remove_attribute (element, "data-inline"); + webkit_dom_element_remove_attribute (element, "data-name"); + } + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogMarkCurrentCellElement") == 0) { + const gchar *element_id; + + g_variant_get (parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_mark_current_cell_element (editor_page, element_id); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSaveHistoryOnExit") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_save_history_on_exit (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementVAlign") == 0) { + const gchar *value; + EContentEditorScope scope; + + g_variant_get (parameters, "(t&si)", &page_id, &value, &scope); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_set_element_v_align (editor_page, value, scope); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementAlign") == 0) { + const gchar *value; + EContentEditorScope scope; + + g_variant_get (parameters, "(t&si)", &page_id, &value, &scope); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_set_element_align (editor_page, value, scope); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementNoWrap") == 0) { + gboolean value; + EContentEditorScope scope; + + g_variant_get (parameters, "(tbi)", &page_id, &value, &scope); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_set_element_no_wrap (editor_page, value, scope); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementHeaderStyle") == 0) { + gboolean value; + EContentEditorScope scope; + + g_variant_get (parameters, "(tbi)", &page_id, &value, &scope); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_set_element_header_style (editor_page, value, scope); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementWidth") == 0) { + const gchar *value; + EContentEditorScope scope; + + g_variant_get (parameters, "(t&si)", &page_id, &value, &scope); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_set_element_width (editor_page, value, scope); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementColSpan") == 0) { + glong value; + EContentEditorScope scope; + + g_variant_get (parameters, "(tii)", &page_id, &value, &scope); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_set_element_col_span (editor_page, value, scope); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementRowSpan") == 0) { + glong value; + EContentEditorScope scope; + + g_variant_get (parameters, "(tii)", &page_id, &value, &scope); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_set_element_row_span (editor_page, value, scope); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementBgColor") == 0) { + const gchar *value; + EContentEditorScope scope; + + g_variant_get (parameters, "(t&si)", &page_id, &value, &scope); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_cell_set_element_bg_color (editor_page, value, scope); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorHRuleDialogFindHRule") == 0) { + gboolean created_new_hr = FALSE; + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + created_new_hr = e_dialogs_dom_h_rule_find_hrule (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(b)", created_new_hr)); + } else if (g_strcmp0 (method_name, "EEditorHRuleDialogOnClose") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_h_rule_dialog_on_close (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorImageDialogMarkImage") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_image_mark_image (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorImageDialogSaveHistoryOnExit") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_image_save_history_on_exit (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorImageDialogSetElementUrl") == 0) { + const gchar *value; + + g_variant_get (parameters, "(t&s)", &page_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_image_set_element_url (editor_page, value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorImageDialogGetElementUrl") == 0) { + gchar *value; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + value = e_dialogs_dom_image_get_element_url (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + value ? value : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "ImageElementSetWidth") == 0) { + const gchar *element_id; + gint32 value; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&si)", &page_id, &element_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + webkit_dom_html_image_element_set_width ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ImageElementGetWidth") == 0) { + const gchar *element_id; + glong value = 0; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_image_element_get_width ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(i)", value)); + } else if (g_strcmp0 (method_name, "ImageElementSetHeight") == 0) { + const gchar *element_id; + gint32 value; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&si)", &page_id, &element_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + webkit_dom_html_image_element_set_width ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ImageElementGetHeight") == 0) { + const gchar *element_id; + glong value = 0; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_image_element_get_height ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(i)", value)); + } else if (g_strcmp0 (method_name, "ImageElementGetNaturalWidth") == 0) { + const gchar *element_id; + glong value = 0; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_image_element_get_natural_width ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(i)", value)); + } else if (g_strcmp0 (method_name, "ImageElementGetNaturalHeight") == 0) { + const gchar *element_id; + glong value = 0; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_image_element_get_natural_height ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(i)", value)); + } else if (g_strcmp0 (method_name, "ImageElementSetHSpace") == 0) { + const gchar *element_id; + gint32 value; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&si)", &page_id, &element_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + webkit_dom_html_image_element_set_hspace ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ImageElementGetHSpace") == 0) { + const gchar *element_id; + glong value = 0; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_image_element_get_hspace ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(i)", value)); + } else if (g_strcmp0 (method_name, "ImageElementSetVSpace") == 0) { + const gchar *element_id; + gint32 value; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&si)", &page_id, &element_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + webkit_dom_html_image_element_set_vspace ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ImageElementGetVSpace") == 0) { + const gchar *element_id; + glong value = 0; + WebKitDOMElement *element; + + g_variant_get ( + parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_image_element_get_vspace ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(i)", value)); + } else if (g_strcmp0 (method_name, "EEditorLinkDialogOk") == 0) { + const gchar *url, *inner_text; + + g_variant_get (parameters, "(t&s&s)", &page_id, &url, &inner_text); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_link_commit (editor_page, url, inner_text); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorLinkDialogShow") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + g_dbus_method_invocation_return_value ( + invocation, e_dialogs_dom_link_show (editor_page)); + } else if (g_strcmp0 (method_name, "EEditorPageDialogSaveHistory") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_page_save_history (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorPageDialogSaveHistoryOnExit") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_page_save_history_on_exit (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorSpellCheckDialogNext") == 0) { + const gchar *from_word = NULL; + const gchar * const *languages = NULL; + gchar *value = NULL; + + g_variant_get (parameters, "(t&s^as)", &page_id, &from_word, &languages); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + value = e_dialogs_dom_spell_check_next (editor_page, from_word, languages); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + value ? value : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "EEditorSpellCheckDialogPrev") == 0) { + const gchar *from_word = NULL; + const gchar * const *languages = NULL; + gchar *value = NULL; + + g_variant_get (parameters, "(t&s^as)", &page_id, &from_word, &languages); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + value = e_dialogs_dom_spell_check_prev (editor_page, from_word, languages); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + value ? value : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "EEditorTableDialogSetRowCount") == 0) { + guint32 value; + + g_variant_get (parameters, "(tu)", &page_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_table_set_row_count (editor_page, value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorTableDialogGetRowCount") == 0) { + gulong value; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + value = e_dialogs_dom_table_get_row_count (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(u)", value)); + } else if (g_strcmp0 (method_name, "EEditorTableDialogSetColumnCount") == 0) { + guint32 value; + + g_variant_get (parameters, "(tu)", &page_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_table_set_column_count (editor_page, value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorTableDialogGetColumnCount") == 0) { + gulong value; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + value = e_dialogs_dom_table_get_column_count (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(u)", value)); + } else if (g_strcmp0 (method_name, "EEditorTableDialogShow") == 0) { + gboolean created_new_table; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + created_new_table = e_dialogs_dom_table_show (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(b)", created_new_table)); + } else if (g_strcmp0 (method_name, "EEditorTableDialogSaveHistoryOnExit") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_table_save_history_on_exit (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorDialogDeleteCellContents") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_delete_cell_contents (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorDialogDeleteColumn") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_delete_column (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorDialogDeleteRow") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_delete_row (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorDialogDeleteTable") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_delete_table (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorDialogInsertColumnAfter") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_insert_column_after (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorDialogInsertColumnBefore") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_insert_column_before (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorDialogInsertRowAbove") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_insert_row_above (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorDialogInsertRowBelow") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_insert_row_below (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorLinkDialogOnOpen") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_link_dialog_on_open (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorLinkDialogOnClose") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_dialogs_dom_link_dialog_on_close (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorLinkDialogUnlink") == 0) { + EEditorUndoRedoManager *manager; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + /* Remove the history event that was saved when the dialog was opened */ + e_editor_undo_redo_manager_remove_current_history_event (manager); + + e_editor_dom_selection_unlink (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EEditorActionsSaveHistoryForCut") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_save_history_for_cut (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "TableCellElementGetNoWrap") == 0) { + const gchar *element_id; + gboolean value = FALSE; + WebKitDOMElement *element; + + g_variant_get (parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_table_cell_element_get_no_wrap ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(b)", value)); + } else if (g_strcmp0 (method_name, "TableCellElementGetRowSpan") == 0) { + const gchar *element_id; + glong value = 0; + WebKitDOMElement *element; + + g_variant_get (parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_table_cell_element_get_row_span ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(i)", value)); + } else if (g_strcmp0 (method_name, "TableCellElementGetColSpan") == 0) { + const gchar *element_id; + glong value = 0; + WebKitDOMElement *element; + + g_variant_get (parameters, "(t&s)", &page_id, &element_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + document = e_editor_page_get_document (editor_page); + element = webkit_dom_document_get_element_by_id (document, element_id); + if (element) + value = webkit_dom_html_table_cell_element_get_col_span ( + WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (element)); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(i)", value)); + } else if (g_strcmp0 (method_name, "DOMSaveSelection") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_save (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMRestoreSelection") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_restore (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMUndo") == 0) { + EEditorUndoRedoManager *manager; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + e_editor_undo_redo_manager_undo (manager); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMRedo") == 0) { + EEditorUndoRedoManager *manager; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + + e_editor_undo_redo_manager_redo (manager); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMTurnSpellCheckOff") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_turn_spell_check_off (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMQuoteAndInsertTextIntoSelection") == 0) { + gboolean is_html = FALSE; + const gchar *text; + + g_variant_get (parameters, "(t&sb)", &page_id, &text, &is_html); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_quote_and_insert_text_into_selection (editor_page, text, is_html); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMConvertAndInsertHTMLIntoSelection") == 0) { + gboolean is_html; + const gchar *text; + + g_variant_get (parameters, "(t&sb)", &page_id, &text, &is_html); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_convert_and_insert_html_into_selection (editor_page, text, is_html); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMEmbedStyleSheet") == 0) { + const gchar *style_sheet_content; + + g_variant_get (parameters, "(t&s)", &page_id, &style_sheet_content); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_embed_style_sheet (editor_page, style_sheet_content); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMRemoveEmbeddedStyleSheet") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_remove_embedded_style_sheet (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "SetPastingContentFromItself") == 0) { + gboolean value = FALSE; + + g_variant_get (parameters, "(tb)", &page_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_page_set_pasting_content_from_itself (editor_page, value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "SetEditorHTMLMode") == 0) { + gboolean html_mode = FALSE; + gboolean convert = FALSE; + + g_variant_get (parameters, "(tbb)", &page_id, &html_mode, &convert); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + convert = convert && e_editor_page_get_html_mode (editor_page) && !html_mode; + e_editor_page_set_html_mode (editor_page, html_mode); + + if (convert) + e_editor_dom_convert_when_changing_composer_mode (editor_page); + else + e_editor_dom_process_content_after_mode_change (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "SetConvertInSitu") == 0) { + gboolean value = FALSE; + + g_variant_get (parameters, "(tb)", &page_id, &value); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_page_set_convert_in_situ (editor_page, value); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMForceSpellCheck") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_force_spell_check (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMCheckIfConversionNeeded") == 0) { + gboolean conversion_needed; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + conversion_needed = e_editor_dom_check_if_conversion_needed (editor_page); + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(b)", conversion_needed)); + } else if (g_strcmp0 (method_name, "DOMGetContent") == 0) { + EContentEditorGetContentFlags flags; + const gchar *from_domain; + gchar *value = NULL; + GVariant *inline_images = NULL; + + g_variant_get (parameters, "(t&si)", &page_id, &from_domain, &flags); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + if ((flags & E_CONTENT_EDITOR_GET_INLINE_IMAGES) && from_domain && *from_domain) + inline_images = e_editor_dom_get_inline_images_data (editor_page, from_domain); + + if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) && + !(flags & E_CONTENT_EDITOR_GET_PROCESSED)) { + value = e_editor_dom_process_content_for_draft ( + editor_page, (flags & E_CONTENT_EDITOR_GET_BODY)); + } else if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) && + (flags & E_CONTENT_EDITOR_GET_PROCESSED) && + !(flags & E_CONTENT_EDITOR_GET_BODY)) { + value = e_editor_dom_process_content_to_html_for_exporting (editor_page); + } else if ((flags & E_CONTENT_EDITOR_GET_TEXT_PLAIN) && + (flags & E_CONTENT_EDITOR_GET_PROCESSED) && + !(flags & E_CONTENT_EDITOR_GET_BODY)) { + value = e_editor_dom_process_content_to_plain_text_for_exporting (editor_page); + } else if ((flags & E_CONTENT_EDITOR_GET_TEXT_PLAIN) && + (flags & E_CONTENT_EDITOR_GET_BODY) && + !(flags & E_CONTENT_EDITOR_GET_PROCESSED)) { + if (flags & E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE) + value = e_composer_dom_get_raw_body_content_without_signature (editor_page); + else + value = e_composer_dom_get_raw_body_content (editor_page); + } else { + g_warning ("Unsupported flags combination (%d) in (%s)", flags, G_STRFUNC); + } + + /* If no inline images are requested we still have to return + * something even it won't be used at all. */ + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(sv)", + value ? value : "", + inline_images ? inline_images : g_variant_new_int32 (0))); + + g_free (value); + + if ((flags & E_CONTENT_EDITOR_GET_INLINE_IMAGES) && from_domain && *from_domain && inline_images) + e_editor_dom_restore_images (editor_page, inline_images); + } else if (g_strcmp0 (method_name, "DOMInsertHTML") == 0) { + const gchar *html; + + g_variant_get (parameters, "(t&s)", &page_id, &html); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_insert_html (editor_page, html); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMConvertContent") == 0) { + const gchar *preferred_text; + + g_variant_get (parameters, "(t&s)", &page_id, &preferred_text); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_convert_content (editor_page, preferred_text); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMAddNewInlineImageIntoList") == 0) { + const gchar *cid_uri, *src, *filename; + + g_variant_get (parameters, "(t&s&s&s)", &page_id, &filename, &cid_uri, &src); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_page_add_new_inline_image_into_list ( + editor_page, cid_uri, src); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMReplaceImageSrc") == 0) { + const gchar *selector, *uri; + + g_variant_get (parameters, "(t&s&s)", &page_id, &selector, &uri); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_replace_image_src (editor_page, selector, uri); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMDragAndDropEnd") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_drag_and_drop_end (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMInsertSmiley") == 0) { + const gchar *smiley_name; + + g_variant_get (parameters, "(t&s)", &page_id, &smiley_name); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_insert_smiley_by_name (editor_page, smiley_name); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMMoveSelectionOnPoint") == 0) { + gboolean cancel_if_not_collapsed; + gint x, y; + + g_variant_get (parameters, "(tiib)", &page_id, &x, &y, &cancel_if_not_collapsed); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + if (cancel_if_not_collapsed) { + if (e_editor_dom_selection_is_collapsed (editor_page)) + e_editor_dom_selection_set_on_point (editor_page, x, y); + } else + e_editor_dom_selection_set_on_point (editor_page, x, y); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionIndent") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_indent (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSave") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_save (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionRestore") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_restore (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionInsertImage") == 0) { + const gchar *uri; + + g_variant_get (parameters, "(t&s)", &page_id, &uri); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_insert_image (editor_page, uri); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionReplace") == 0) { + const gchar *replacement; + + g_variant_get (parameters, "(t&s)", &page_id, &replacement); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_replace (editor_page, replacement); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetAlignment") == 0) { + EContentEditorAlignment alignment; + + g_variant_get (parameters, "(ti)", &page_id, &alignment); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_set_alignment (editor_page, alignment); + e_editor_page_set_alignment (editor_page, alignment); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetBold") == 0) { + gboolean bold; + + g_variant_get (parameters, "(tb)", &page_id, &bold); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_page_set_bold (editor_page, bold); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetBlockFormat") == 0) { + EContentEditorBlockFormat block_format; + + g_variant_get (parameters, "(ti)", &page_id, &block_format); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_set_block_format (editor_page, block_format); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetFontColor") == 0) { + const gchar *color; + + g_variant_get (parameters, "(t&s)", &page_id, &color); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_set_font_color (editor_page, color); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetFontSize") == 0) { + EContentEditorFontSize font_size; + + g_variant_get (parameters, "(ti)", &page_id, &font_size); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_set_font_size (editor_page, font_size); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetItalic") == 0) { + gboolean italic; + + g_variant_get (parameters, "(tb)", &page_id, &italic); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_page_set_italic (editor_page, italic); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetMonospaced") == 0) { + gboolean monospaced; + + g_variant_get (parameters, "(tb)", &page_id, &monospaced); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_page_set_monospace (editor_page, monospaced); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetStrikethrough") == 0) { + gboolean strikethrough; + + g_variant_get (parameters, "(tb)", &page_id, &strikethrough); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_page_set_strikethrough (editor_page, strikethrough); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetSubscript") == 0) { + gboolean subscript; + + g_variant_get (parameters, "(tb)", &page_id, &subscript); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_set_subscript (editor_page, subscript); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetSuperscript") == 0) { + gboolean superscript; + + g_variant_get (parameters, "(tb)", &page_id, &superscript); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_set_superscript (editor_page, superscript); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionSetUnderline") == 0) { + gboolean underline; + + g_variant_get (parameters, "(tb)", &page_id, &underline); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_page_set_underline (editor_page, underline); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionUnindent") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_unindent (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMSelectionWrap") == 0) { + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_editor_dom_selection_wrap (editor_page); + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMGetCaretWord") == 0) { + gchar *word; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + word = e_editor_dom_get_caret_word (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + word ? word : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "DOMInsertSignature") == 0) { + gboolean is_html, set_signature_from_message; + gboolean check_if_signature_is_changed, ignore_next_signature_change; + const gchar *content, *signature_id; + gchar *new_signature_id = NULL; + + g_variant_get ( + parameters, + "(t&sb&sbbb)", + &page_id, + &content, + &is_html, + &signature_id, + &set_signature_from_message, + &check_if_signature_is_changed, + &ignore_next_signature_change); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + new_signature_id = e_composer_dom_insert_signature ( + editor_page, + content, + is_html, + signature_id, + &set_signature_from_message, + &check_if_signature_is_changed, + &ignore_next_signature_change); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(sbbb)", + new_signature_id ? new_signature_id : "", + set_signature_from_message, + check_if_signature_is_changed, + ignore_next_signature_change)); + + g_free (new_signature_id); + } else if (g_strcmp0 (method_name, "DOMSaveDragAndDropHistory") == 0) { + g_variant_get ( + parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_composer_dom_save_drag_and_drop_history (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMCleanAfterDragAndDrop") == 0) { + g_variant_get ( + parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + e_composer_dom_clean_after_drag_and_drop (editor_page); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DOMGetActiveSignatureUid") == 0) { + gchar *value; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + value = e_composer_dom_get_active_signature_uid (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + value ? value : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "DOMGetCaretPosition") == 0) { + guint32 value; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + value = e_editor_dom_get_caret_position (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, + value ? g_variant_new_uint32 (value) : NULL); + } else if (g_strcmp0 (method_name, "DOMGetCaretOffset") == 0) { + guint32 value; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + value = e_editor_dom_get_caret_offset (editor_page); + + g_dbus_method_invocation_return_value ( + invocation, + value ? g_variant_new_uint32 (value) : NULL); + } else if (g_strcmp0 (method_name, "DOMClearUndoRedoHistory") == 0) { + EEditorUndoRedoManager *manager; + + g_variant_get (parameters, "(t)", &page_id); + + editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id); + if (!editor_page) + goto error; + + manager = e_editor_page_get_undo_redo_manager (editor_page); + if (manager) + e_editor_undo_redo_manager_clean_history (manager); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else { + g_warning ("UNKNOWN METHOD '%s'", method_name); + } + + return; + + error: + g_warning ("Cannot obtain WebKitWebPage for '%ld'", page_id); +} + +static void +web_page_gone_cb (gpointer user_data, + GObject *gone_web_page) +{ + EEditorWebExtension *extension = user_data; + GHashTableIter iter; + gpointer key, value; + + g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension)); + + g_hash_table_iter_init (&iter, extension->priv->editor_pages); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (value == gone_web_page) { + g_hash_table_remove (extension->priv->editor_pages, key); + break; + } + } +} + +static const GDBusInterfaceVTable interface_vtable = { + handle_method_call, + NULL, + NULL +}; + +static void +e_editor_web_extension_dispose (GObject *object) +{ + EEditorWebExtension *extension = E_EDITOR_WEB_EXTENSION (object); + + if (extension->priv->dbus_connection) { + g_dbus_connection_unregister_object ( + extension->priv->dbus_connection, + extension->priv->registration_id); + extension->priv->registration_id = 0; + extension->priv->dbus_connection = NULL; + } + + g_hash_table_remove_all (extension->priv->editor_pages); + + g_clear_object (&extension->priv->wk_extension); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_editor_web_extension_parent_class)->dispose (object); +} + +static void +e_editor_web_extension_finalize (GObject *object) +{ + EEditorWebExtension *extension = E_EDITOR_WEB_EXTENSION (object); + + if (extension->priv->editor_pages) { + g_hash_table_destroy (extension->priv->editor_pages); + extension->priv->editor_pages = NULL; + } + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_editor_web_extension_parent_class)->finalize (object); +} + +static void +e_editor_web_extension_class_init (EEditorWebExtensionClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = e_editor_web_extension_dispose; + object_class->finalize = e_editor_web_extension_finalize; + + g_type_class_add_private (object_class, sizeof(EEditorWebExtensionPrivate)); +} + +static void +e_editor_web_extension_init (EEditorWebExtension *extension) +{ + extension->priv = E_EDITOR_WEB_EXTENSION_GET_PRIVATE (extension); + extension->priv->editor_pages = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_object_unref); +} + +static gpointer +e_editor_web_extension_create_instance(gpointer data) +{ + return g_object_new (E_TYPE_EDITOR_WEB_EXTENSION, NULL); +} + +EEditorWebExtension * +e_editor_web_extension_get_default (void) +{ + static GOnce once_init = G_ONCE_INIT; + return E_EDITOR_WEB_EXTENSION (g_once (&once_init, e_editor_web_extension_create_instance, NULL)); +} + +static gboolean +image_exists_in_cache (const gchar *image_uri) +{ + gchar *filename; + gchar *hash; + gboolean exists = FALSE; + + if (!emd_global_http_cache) + return FALSE; + + hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1); + filename = camel_data_cache_get_filename ( + emd_global_http_cache, "http", hash); + + if (filename != NULL) { + struct stat st; + + exists = g_file_test (filename, G_FILE_TEST_EXISTS); + if (exists && g_stat (filename, &st) == 0) { + exists = st.st_size != 0; + } else { + exists = FALSE; + } + g_free (filename); + } + + g_free (hash); + + return exists; +} + +static void +redirect_http_uri (EEditorWebExtension *extension, + WebKitWebPage *web_page, + WebKitURIRequest *request) +{ + const gchar *uri; + gchar *new_uri; + SoupURI *soup_uri; + gboolean image_exists; + EEditorPage *editor_page; + EImageLoadingPolicy image_policy; + + editor_page = get_editor_page (extension, webkit_web_page_get_id (web_page)); + g_return_if_fail (E_IS_EDITOR_PAGE (editor_page)); + + uri = webkit_uri_request_get_uri (request); + + /* Check Evolution's cache */ + image_exists = image_exists_in_cache (uri); + + /* If the URI is not cached and we are not allowed to load it + * then redirect to invalid URI, so that webkit would display + * a native placeholder for it. */ + image_policy = e_editor_page_get_image_loading_policy (editor_page); + if (!image_exists && !e_editor_page_get_force_image_load (editor_page) && + (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) { + webkit_uri_request_set_uri (request, "about:blank"); + return; + } + + new_uri = g_strconcat ("evo-", uri, NULL); + soup_uri = soup_uri_new (new_uri); + g_free (new_uri); + + new_uri = soup_uri_to_string (soup_uri, FALSE); + webkit_uri_request_set_uri (request, new_uri); + soup_uri_free (soup_uri); + + g_free (new_uri); +} + +static gboolean +web_page_send_request_cb (WebKitWebPage *web_page, + WebKitURIRequest *request, + WebKitURIResponse *redirected_response, + EEditorWebExtension *extension) +{ + const char *request_uri; + const char *page_uri; + gboolean uri_is_http; + + request_uri = webkit_uri_request_get_uri (request); + page_uri = webkit_web_page_get_uri (web_page); + + /* Always load the main resource. */ + if (g_strcmp0 (request_uri, page_uri) == 0) + return FALSE; + + uri_is_http = + g_str_has_prefix (request_uri, "http:") || + g_str_has_prefix (request_uri, "https:") || + g_str_has_prefix (request_uri, "evo-http:") || + g_str_has_prefix (request_uri, "evo-https:"); + + if (uri_is_http) + redirect_http_uri (extension, web_page, request); + + return FALSE; +} + +static void +web_page_created_cb (WebKitWebExtension *wk_extension, + WebKitWebPage *web_page, + EEditorWebExtension *extension) +{ + EEditorPage *editor_page; + guint64 *ppage_id; + + g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page)); + g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension)); + + ppage_id = g_new (guint64, 1); + *ppage_id = webkit_web_page_get_id (web_page); + + editor_page = e_editor_page_new (web_page, extension); + g_hash_table_insert (extension->priv->editor_pages, ppage_id, editor_page); + + g_object_weak_ref (G_OBJECT (web_page), web_page_gone_cb, extension); + + g_signal_connect ( + web_page, "send-request", + G_CALLBACK (web_page_send_request_cb), extension); +} + +void +e_editor_web_extension_initialize (EEditorWebExtension *extension, + WebKitWebExtension *wk_extension) +{ + g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension)); + + extension->priv->wk_extension = g_object_ref (wk_extension); + + if (emd_global_http_cache == NULL) { + emd_global_http_cache = camel_data_cache_new ( + e_get_user_cache_dir (), NULL); + + if (emd_global_http_cache) { + /* cache expiry - 2 hour access, 1 day max */ + camel_data_cache_set_expire_age ( + emd_global_http_cache, 24 * 60 * 60); + camel_data_cache_set_expire_access ( + emd_global_http_cache, 2 * 60 * 60); + } + } + + g_signal_connect ( + wk_extension, "page-created", + G_CALLBACK (web_page_created_cb), extension); +} + +void +e_editor_web_extension_dbus_register (EEditorWebExtension *extension, + GDBusConnection *connection) +{ + GError *error = NULL; + static GDBusNodeInfo *introspection_data = NULL; + + g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension)); + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + + if (!introspection_data) { + introspection_data = + g_dbus_node_info_new_for_xml (introspection_xml, NULL); + + extension->priv->registration_id = + g_dbus_connection_register_object ( + connection, + E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, + introspection_data->interfaces[0], + &interface_vtable, + extension, + NULL, + &error); + + if (!extension->priv->registration_id) { + g_warning ("Failed to register object: %s\n", error->message); + g_error_free (error); + } else { + extension->priv->dbus_connection = connection; + g_object_add_weak_pointer ( + G_OBJECT (connection), + (gpointer *) &extension->priv->dbus_connection); + } + } +} + +GDBusConnection * +e_editor_web_extension_get_connection (EEditorWebExtension *extension) +{ + g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL); + + return extension->priv->dbus_connection; +} diff --git a/modules/webkit-editor/web-extension/e-editor-web-extension.h b/modules/webkit-editor/web-extension/e-editor-web-extension.h new file mode 100644 index 0000000..987b3de --- /dev/null +++ b/modules/webkit-editor/web-extension/e-editor-web-extension.h @@ -0,0 +1,82 @@ +/* + * e-editor-web-extension.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_EDITOR_WEB_EXTENSION_H +#define E_EDITOR_WEB_EXTENSION_H + +#include <glib-object.h> +#include <webkit2/webkit-web-extension.h> + +#define E_UTIL_INCLUDE_WITHOUT_WEBKIT +#include <e-util/e-util.h> +#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT + +#include "e-editor-web-extension-names.h" + +/* Standard GObject macros */ +#define E_TYPE_EDITOR_WEB_EXTENSION \ + (e_editor_web_extension_get_type ()) +#define E_EDITOR_WEB_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_EDITOR_WEB_EXTENSION, EEditorWebExtension)) +#define E_EDITOR_WEB_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_EDITOR_WEB_EXTENSION, EEditorWebExtensionClass)) +#define E_IS_EDITOR_WEB_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_EDITOR_WEB_EXTENSION)) +#define E_IS_EDITOR_WEB_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_EDITOR_WEB_EXTENSION)) +#define E_EDITOR_WEB_EXTENSION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_EDITOR_WEB_EXTENSION, EEditorWebExtensionClass)) + +G_BEGIN_DECLS + +typedef struct _EEditorWebExtension EEditorWebExtension; +typedef struct _EEditorWebExtensionClass EEditorWebExtensionClass; +typedef struct _EEditorWebExtensionPrivate EEditorWebExtensionPrivate; + +struct _EEditorWebExtension { + GObject parent; + EEditorWebExtensionPrivate *priv; +}; + +struct _EEditorWebExtensionClass +{ + GObjectClass parent_class; +}; + +GType e_editor_web_extension_get_type (void) G_GNUC_CONST; + +EEditorWebExtension * + e_editor_web_extension_get_default + (void); + +void e_editor_web_extension_initialize + (EEditorWebExtension *extension, + WebKitWebExtension *wk_extension); +void e_editor_web_extension_dbus_register + (EEditorWebExtension *extension, + GDBusConnection *connection); +GDBusConnection * + e_editor_web_extension_get_connection + (EEditorWebExtension *extension); + +#endif /* E_EDITOR_WEB_EXTENSION_H */ diff --git a/modules/web-inspector/Makefile.am b/modules/webkit-inspector/Makefile.am index 237121e..eedaf12 100644 --- a/modules/web-inspector/Makefile.am +++ b/modules/webkit-inspector/Makefile.am @@ -1,24 +1,24 @@ -module_LTLIBRARIES = module-web-inspector.la +module_LTLIBRARIES = module-webkit-inspector.la -module_web_inspector_la_CPPFLAGS = \ +module_webkit_inspector_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ -I$(top_srcdir) \ - -DG_LOG_DOMAIN=\"evolution-web-inspector\" \ + -DG_LOG_DOMAIN=\"evolution-webkit-inspector\" \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ $(CODE_COVERAGE_CFLAGS) \ $(NULL) -module_web_inspector_la_SOURCES = \ - evolution-web-inspector.c +module_webkit_inspector_la_SOURCES = \ + evolution-webkit-inspector.c -module_web_inspector_la_LIBADD = \ +module_webkit_inspector_la_LIBADD = \ $(top_builddir)/e-util/libevolution-util.la \ $(EVOLUTION_DATA_SERVER_LIBS) \ $(GNOME_PLATFORM_LIBS) \ $(NULL) -module_web_inspector_la_LDFLAGS = \ +module_webkit_inspector_la_LDFLAGS = \ -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS) -include $(top_srcdir)/git.mk diff --git a/modules/webkit-inspector/evolution-webkit-inspector.c b/modules/webkit-inspector/evolution-webkit-inspector.c new file mode 100644 index 0000000..4c9a141 --- /dev/null +++ b/modules/webkit-inspector/evolution-webkit-inspector.c @@ -0,0 +1,140 @@ +/* + * evolution-webkit-inspector.c + * + * 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. + * + * 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 General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> +#include <glib/gi18n-lib.h> +#include <gdk/gdkkeysyms.h> + +#include <libebackend/libebackend.h> + +#include <e-util/e-util.h> + +/* Standard GObject macros */ +#define E_TYPE_WEBKIT_INSPECTOR \ + (e_webkit_inspector_get_type ()) +#define E_WEBKIT_INSPECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_WEBKIT_INSPECTOR, EWebKitInspector)) + +/* <Control>+<Shift>+I */ +#define WEBKIT_INSPECTOR_MOD (GDK_CONTROL_MASK | GDK_SHIFT_MASK) +#define WEBKIT_INSPECTOR_KEY (GDK_KEY_I) + +#define WEBKIT_INSPECTOR_SHORTCUT_SHOW(event) \ + ((((event)->state & WEBKIT_INSPECTOR_MOD) == WEBKIT_INSPECTOR_MOD) && \ + ((event)->keyval == WEBKIT_INSPECTOR_KEY)) + +typedef struct _EWebKitInspector EWebKitInspector; +typedef struct _EWebKitInspectorClass EWebKitInspectorClass; + +struct _EWebKitInspector { + EExtension parent; +}; + +struct _EWebKitInspectorClass { + EExtensionClass parent_class; +}; + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); + +/* Forward Declarations */ +GType e_webkit_inspector_get_type (void); + +G_DEFINE_DYNAMIC_TYPE (EWebKitInspector, e_webkit_inspector, E_TYPE_EXTENSION) + +static WebKitWebView * +webkit_inspector_get_web_view (EWebKitInspector *extension) +{ + EExtensible *extensible; + + extensible = e_extension_get_extensible (E_EXTENSION (extension)); + + return WEBKIT_WEB_VIEW (extensible); +} + +static gboolean +webkit_inspector_key_press_event_cb (WebKitWebView *web_view, + GdkEventKey *event) +{ + WebKitWebInspector *inspector; + gboolean handled = FALSE; + + inspector = webkit_web_view_get_inspector (web_view); + + if (WEBKIT_INSPECTOR_SHORTCUT_SHOW (event)) { + webkit_web_inspector_show (inspector); + handled = TRUE; + } + + return handled; +} + +static void +webkit_inspector_constructed (GObject *object) +{ + EWebKitInspector *extension; + WebKitWebView *web_view; + WebKitSettings *settings; + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_webkit_inspector_parent_class)->constructed (object); + + extension = E_WEBKIT_INSPECTOR (object); + web_view = webkit_inspector_get_web_view (extension); + settings = webkit_web_view_get_settings (web_view); + webkit_settings_set_enable_developer_extras (settings, TRUE); + + g_signal_connect ( + web_view, "key-press-event", + G_CALLBACK (webkit_inspector_key_press_event_cb), NULL); +} + +static void +e_webkit_inspector_class_init (EWebKitInspectorClass *class) +{ + GObjectClass *object_class; + EExtensionClass *extension_class; + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = webkit_inspector_constructed; + + extension_class = E_EXTENSION_CLASS (class); + extension_class->extensible_type = WEBKIT_TYPE_WEB_VIEW; +} + +static void +e_webkit_inspector_class_finalize (EWebKitInspectorClass *class) +{ +} + +static void +e_webkit_inspector_init (EWebKitInspector *extension) +{ +} + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + e_webkit_inspector_register_type (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} diff --git a/plugins/external-editor/external-editor.c b/plugins/external-editor/external-editor.c index 2709c84..f349b5f 100644 --- a/plugins/external-editor/external-editor.c +++ b/plugins/external-editor/external-editor.c @@ -150,16 +150,16 @@ enable_disable_composer (EMsgComposer *composer, gboolean enable) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; GtkAction *action; GtkActionGroup *action_group; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); - webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), enable); + e_content_editor_set_editable (cnt_editor, enable); action = E_HTML_EDITOR_ACTION_EDIT_MENU (editor); gtk_action_set_sensitive (action, enable); @@ -192,20 +192,20 @@ update_composer_text (GArray *array) { EMsgComposer *composer; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; gchar *text; composer = g_array_index (array, gpointer, 0); text = g_array_index (array, gpointer, 1); editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); e_msg_composer_set_body_text (composer, text, FALSE); enable_composer (composer); - e_html_editor_view_set_changed (view, TRUE); + e_content_editor_set_changed (cnt_editor, TRUE); g_free (text); @@ -239,117 +239,19 @@ numlines (const gchar *text, gint ctr = 0; gint lineno = 0; - while (text && *text && ctr < pos) { - if (*text == '\n') { - /* Because the get_caret_position is returning a position - * without new lines, we can't increment the counter when - * we hit the new line. */ + while (text && *text && ctr <= pos) { + if (*text == '\n') lineno++; - } else { - ctr++; - } - text++; + text++; + ctr++; } - if (lineno > 0) + if (lineno > 0) { lineno++; - - return lineno; -} - -static gint -get_caret_offset (EHTMLEditorView *view) -{ - gint ret_val; - gchar *text; - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMNode *anchor; - WebKitDOMRange *range; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { - g_object_unref (dom_selection); - return 0; } - webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL); - /* Select the text from the current caret position to the beginning of the line. */ - webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "lineBoundary"); - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection); - text = webkit_dom_range_to_string (range, NULL); - ret_val = strlen (text); - g_free (text); - - webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL); - - /* In the plain text mode we need to increase the return value by 2 per - * citation level because of "> ". */ - if (!e_html_editor_view_get_html_mode (view)) { - WebKitDOMNode *parent = anchor; - - while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && - element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-plaintext-quoted")) - ret_val += 2; - - parent = webkit_dom_node_get_parent_node (parent); - } - } - - g_object_unref (range); - g_object_unref (dom_selection); - - return ret_val; -} - -static gint -get_caret_position (EHTMLEditorView *view) -{ - gint ret_val; - gchar *text; - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - WebKitDOMDOMWindow *dom_window; - WebKitDOMDOMSelection *dom_selection; - WebKitDOMRange *range, *range_clone; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - dom_window = webkit_dom_document_get_default_view (document); - dom_selection = webkit_dom_dom_window_get_selection (dom_window); - g_object_unref (dom_window); - - if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) { - g_object_unref (dom_selection); - return 0; - } - - range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); - range_clone = webkit_dom_range_clone_range (range, NULL); - - body = webkit_dom_document_get_body (document); - /* Select the text from the beginning of the body to the current caret. */ - webkit_dom_range_set_start_before ( - range_clone, webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), NULL); - - /* This is returning a text without new lines! */ - text = webkit_dom_range_to_string (range_clone, NULL); - ret_val = strlen (text); - g_free (text); - - g_object_unref (range_clone); - g_object_unref (range); - g_object_unref (dom_selection); - - return ret_val; + return lineno; } static gboolean external_editor_running = FALSE; @@ -365,10 +267,10 @@ external_editor_thread (gpointer user_data) gchar *editor_cmd_line = NULL, *editor_cmd = NULL, *content; gint fd, position = -1, offset = -1; EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); /* prefix temp files with evo so .*vimrc can be setup to recognize them */ fd = g_file_open_tmp ("evoXXXXXX", &filename, NULL); @@ -377,8 +279,13 @@ external_editor_thread (gpointer user_data) d (printf ("\n\aTemporary-file Name is : [%s] \n\a", filename)); /* Push the text (if there is one) from the composer to the file */ - content = e_html_editor_view_get_text_plain (view); - g_file_set_contents (filename, content, strlen (content), NULL); + content = e_content_editor_get_content ( + cnt_editor, + E_CONTENT_EDITOR_GET_TEXT_PLAIN | + E_CONTENT_EDITOR_GET_PROCESSED, + NULL, NULL); + if (content && *content) + g_file_set_contents (filename, content, strlen (content), NULL); } else { struct run_error_dialog_data *data; @@ -406,14 +313,14 @@ external_editor_thread (gpointer user_data) g_object_unref (settings); if (g_strrstr (editor_cmd, "vim") != NULL && - ((position = get_caret_position (view)) > 0)) { + ((position = e_content_editor_get_caret_position (cnt_editor)) > 0)) { gchar *tmp = editor_cmd; gint lineno; gboolean set_nofork; set_nofork = g_strrstr (editor_cmd, "gvim") != NULL; - offset = get_caret_offset (view); + offset = e_content_editor_get_caret_offset (cnt_editor); /* Increment by 1 so that entering vim insert mode places you * in the same entry position you were at in the html. */ offset++; @@ -500,7 +407,7 @@ finished: static void launch_editor (GtkAction *action, EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; d (printf ("\n\nexternal_editor plugin is launched \n\n")); @@ -510,9 +417,9 @@ static void launch_editor (GtkAction *action, EMsgComposer *composer) } editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); - e_html_editor_view_clear_history (view); + e_content_editor_clear_undo_redo_history (cnt_editor); disable_composer (composer); g_mutex_lock (&external_editor_running_lock); @@ -596,10 +503,10 @@ e_plugin_ui_init (GtkUIManager *manager, EMsgComposer *composer) { EHTMLEditor *editor; - EHTMLEditorView *view; + EContentEditor *cnt_editor; editor = e_msg_composer_get_editor (composer); - view = e_html_editor_get_view (editor); + cnt_editor = e_html_editor_get_content_editor (editor); /* Add actions to the "composer" action group. */ gtk_action_group_add_actions ( @@ -607,11 +514,11 @@ e_plugin_ui_init (GtkUIManager *manager, entries, G_N_ELEMENTS (entries), composer); g_signal_connect ( - view, "key_press_event", + cnt_editor, "key_press_event", G_CALLBACK (key_press_cb), composer); g_signal_connect ( - view, "delete-event", + cnt_editor, "delete-event", G_CALLBACK (delete_cb), composer); return TRUE; diff --git a/plugins/mail-to-task/mail-to-task.c b/plugins/mail-to-task/mail-to-task.c index 7774044..ef0fda4 100644 --- a/plugins/mail-to-task/mail-to-task.c +++ b/plugins/mail-to-task/mail-to-task.c @@ -799,7 +799,7 @@ typedef struct { ECalClientSourceType source_type; CamelFolder *folder; GPtrArray *uids; - gchar *selected_text; + const gchar *selected_text; gboolean with_attendees; }AsyncData; @@ -1009,7 +1009,6 @@ do_mail_to_event (AsyncData *data) g_object_unref (data->client_cache); g_object_unref (data->source); - g_free (data->selected_text); g_free (data); data = NULL; @@ -1045,24 +1044,21 @@ text_contains_nonwhitespace (const gchar *text, return p - text < len - 1 && c != 0; } -/* should be freed with g_free after done with it */ -static gchar * +static const gchar * get_selected_text (EMailReader *reader) { EMailDisplay *display; - gchar *text = NULL; + const gchar *text = NULL; display = e_mail_reader_get_mail_display (reader); if (!e_web_view_is_selection_active (E_WEB_VIEW (display))) return NULL; - text = e_mail_display_get_selection_plain_text (display); + text = e_mail_display_get_selection_plain_text_sync (display, NULL, NULL); - if (text == NULL || !text_contains_nonwhitespace (text, strlen (text))) { - g_free (text); + if (text == NULL || !text_contains_nonwhitespace (text, strlen (text))) return NULL; - } return text; } diff --git a/plugins/mailing-list-actions/mailing-list-actions.c b/plugins/mailing-list-actions/mailing-list-actions.c index 498a94d..58f58f6 100644 --- a/plugins/mailing-list-actions/mailing-list-actions.c +++ b/plugins/mailing-list-actions/mailing-list-actions.c @@ -116,6 +116,44 @@ async_context_free (AsyncContext *context) g_slice_free (AsyncContext, context); } +typedef struct _SendMessageData { + gchar *url; + gchar *uid; +} SendMessageData; + +static void +send_message_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + SendMessageData *smd = user_data; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (smd != NULL); + + composer = e_msg_composer_new_finish (result, &error); + if (error) { + g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } else { + EComposerHeaderTable *table; + + /* directly send message */ + e_msg_composer_setup_from_url (composer, smd->url); + table = e_msg_composer_get_header_table (composer); + + if (smd->uid) + e_composer_header_table_set_identity_uid (table, smd->uid); + + e_msg_composer_send (composer); + } + + g_free (smd->url); + g_free (smd->uid); + g_free (smd); +} + static void emla_list_action_cb (CamelFolder *folder, GAsyncResult *result, @@ -124,7 +162,6 @@ emla_list_action_cb (CamelFolder *folder, const gchar *header = NULL, *headerpos; gchar *end, *url = NULL; gint t; - EMsgComposer *composer; EAlertSink *alert_sink; CamelMimeMessage *message; gint send_message_response; @@ -239,15 +276,13 @@ emla_list_action_cb (CamelFolder *folder, url, NULL); if (send_message_response == GTK_RESPONSE_YES) { - EComposerHeaderTable *table; + SendMessageData *smd; - /* directly send message */ - composer = e_msg_composer_new_from_url (shell, url); - table = e_msg_composer_get_header_table (composer); + smd = g_new0 (SendMessageData, 1); + smd->url = g_strdup (url); + smd->uid = g_strdup (uid); - if (uid != NULL) - e_composer_header_table_set_identity_uid (table, uid); - e_msg_composer_send (composer); + e_msg_composer_new (shell, send_message_composer_created_cb, smd); } else if (send_message_response == GTK_RESPONSE_NO) { /* show composer */ em_utils_compose_new_message_with_mailto (shell, url, folder); diff --git a/plugins/templates/templates.c b/plugins/templates/templates.c index 57229dd..3f98a6e 100644 --- a/plugins/templates/templates.c +++ b/plugins/templates/templates.c @@ -51,6 +51,7 @@ struct _AsyncContext { EActivity *activity; EMailReader *reader; CamelMimeMessage *message; + CamelMimeMessage *template; CamelFolder *template_folder; gchar *source_folder_uri; gchar *message_uid; @@ -123,17 +124,11 @@ static gboolean plugin_enabled; static void async_context_free (AsyncContext *context) { - if (context->activity != NULL) - g_object_unref (context->activity); - - if (context->reader != NULL) - g_object_unref (context->reader); - - if (context->message != NULL) - g_object_unref (context->message); - - if (context->template_folder != NULL) - g_object_unref (context->template_folder); + g_clear_object (&context->activity); + g_clear_object (&context->reader); + g_clear_object (&context->message); + g_clear_object (&context->template); + g_clear_object (&context->template_folder); g_free (context->source_folder_uri); g_free (context->message_uid); @@ -846,10 +841,11 @@ find_template_part_in_multipart (CamelMultipart *multipart, } static void -create_new_message (CamelFolder *folder, - GAsyncResult *result, - AsyncContext *context) +create_new_message_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { + AsyncContext *context = user_data; EAlertSink *alert_sink; CamelMimeMessage *new; CamelMimeMessage *message; @@ -859,24 +855,26 @@ create_new_message (CamelFolder *folder, struct _camel_header_raw *header; EMailBackend *backend; EMailSession *session; - EShell *shell; const gchar *message_uid; EMsgComposer *composer; GError *error = NULL; CamelMimePart *template_part = NULL; + CamelFolder *folder; + + g_return_if_fail (context != NULL); alert_sink = e_activity_get_alert_sink (context->activity); - template = camel_folder_get_message_finish (folder, result, &error); + composer = e_msg_composer_new_finish (result, &error); if (e_activity_handle_cancellation (context->activity, error)) { - g_warn_if_fail (template == NULL); + g_warn_if_fail (context->template == NULL); async_context_free (context); g_error_free (error); return; } else if (error != NULL) { - g_warn_if_fail (template == NULL); + g_warn_if_fail (context->template == NULL); e_alert_submit ( alert_sink, "mail:no-retrieve-message", error->message, NULL); @@ -885,17 +883,16 @@ create_new_message (CamelFolder *folder, return; } - g_return_if_fail (CAMEL_IS_MIME_MESSAGE (template)); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); message = context->message; message_uid = context->message_uid; + template = context->template; backend = e_mail_reader_get_backend (context->reader); session = e_mail_backend_get_session (backend); - shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); - folder = e_mail_session_get_local_folder ( - session, E_MAIL_LOCAL_FOLDER_TEMPLATES); + folder = e_mail_session_get_local_folder (session, E_MAIL_LOCAL_FOLDER_TEMPLATES); new = camel_mime_message_new (); new_multipart = camel_multipart_new (); @@ -989,14 +986,12 @@ create_new_message (CamelFolder *folder, template, CAMEL_RECIPIENT_TYPE_BCC)); /* Create the composer */ - composer = em_utils_edit_message ( - shell, folder, new, message_uid, TRUE); + em_utils_edit_message (composer, folder, new, message_uid, TRUE); if (composer && context->source_folder_uri && context->message_uid) e_msg_composer_set_source_headers ( composer, context->source_folder_uri, context->message_uid, CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN); - g_object_unref (template); g_object_unref (new_multipart); g_object_unref (new); @@ -1004,6 +999,51 @@ create_new_message (CamelFolder *folder, } static void +create_new_message (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + AsyncContext *context = user_data; + EAlertSink *alert_sink; + EMailBackend *backend; + EShell *shell; + CamelFolder *folder; + GError *error = NULL; + + g_return_if_fail (CAMEL_IS_FOLDER (source_object)); + g_return_if_fail (context != NULL); + + folder = CAMEL_FOLDER (source_object); + + alert_sink = e_activity_get_alert_sink (context->activity); + + context->template = camel_folder_get_message_finish (folder, result, &error); + + if (e_activity_handle_cancellation (context->activity, error)) { + g_warn_if_fail (context->template == NULL); + async_context_free (context); + g_error_free (error); + return; + + } else if (error != NULL) { + g_warn_if_fail (context->template == NULL); + e_alert_submit ( + alert_sink, "mail:no-retrieve-message", + error->message, NULL); + async_context_free (context); + g_error_free (error); + return; + } + + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (context->template)); + + backend = e_mail_reader_get_backend (context->reader); + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); + + e_msg_composer_new (shell, create_new_message_composer_created_cb, context); +} + +static void template_got_source_message (CamelFolder *folder, GAsyncResult *result, AsyncContext *context) @@ -1044,8 +1084,7 @@ template_got_source_message (CamelFolder *folder, context->template_folder, context->template_message_uid, G_PRIORITY_DEFAULT, cancellable, - (GAsyncReadyCallback) create_new_message, - context); + create_new_message, context); } static void diff --git a/po/POTFILES.in b/po/POTFILES.in index b536530..218cb2d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -145,6 +145,7 @@ em-format/e-mail-parser-message-external.c em-format/e-mail-parser-multipart-encrypted.c em-format/e-mail-parser-multipart-signed.c em-format/e-mail-part-headers.c +em-format/e-mail-part-secure-button.c em-format/e-mail-part-utils.c e-util/ea-calendar-item.c e-util/e-action-combo-box.c @@ -213,7 +214,6 @@ e-util/e-html-editor-replace-dialog.c e-util/e-html-editor-spell-check-dialog.c e-util/e-html-editor-table-dialog.c e-util/e-html-editor-text-dialog.c -e-util/e-html-editor-view.c e-util/e-image-chooser.c e-util/e-image-chooser-dialog.c e-util/e-import-assistant.c @@ -463,7 +463,7 @@ modules/text-highlight/languages.c modules/vcard-inline/e-mail-formatter-vcard.c modules/vcard-inline/e-mail-parser-vcard.c modules/vcard-inline/e-mail-part-vcard.c -modules/web-inspector/evolution-web-inspector.c +modules/webkit-inspector/evolution-webkit-inspector.c plugins/attachment-reminder/attachment-reminder.c plugins/attachment-reminder/org-gnome-attachment-reminder.error.xml plugins/attachment-reminder/org-gnome-evolution-attachment-reminder.eplug.xml diff --git a/shell/e-shell.c b/shell/e-shell.c index 30c4670..c3d0be6 100644 --- a/shell/e-shell.c +++ b/shell/e-shell.c @@ -1628,13 +1628,13 @@ shell_initable_init (GInitable *initable, /* Configure WebKit's default SoupSession. */ proxy_source = e_source_registry_ref_builtin_proxy (registry); - +/* FIXME WK2 g_object_set ( webkit_get_default_session (), SOUP_SESSION_PROXY_RESOLVER, G_PROXY_RESOLVER (proxy_source), NULL); - +*/ g_object_unref (proxy_source); g_object_unref (registry); diff --git a/shell/main.c b/shell/main.c index 1083c7d..f472b2c 100644 --- a/shell/main.c +++ b/shell/main.c @@ -52,7 +52,7 @@ #include <libxml/parser.h> #include <libxml/tree.h> -#include <webkit/webkit.h> +#include <webkit2/webkit2.h> #include "e-shell.h" #include "e-shell-migrate.h" @@ -569,6 +569,7 @@ main (gint argc, handle_term_signal, NULL, NULL); #endif + e_util_init_main_thread (NULL); e_passwords_init (); gtk_window_set_default_icon_name ("evolution"); @@ -592,6 +593,7 @@ main (gint argc, g_object_unref (settings); #endif + /* FIXME WK2 - Look if we still need this it looks like it's not. */ /* Workaround https://bugzilla.gnome.org/show_bug.cgi?id=683548 */ if (!quit) g_type_ensure (WEBKIT_TYPE_WEB_VIEW); diff --git a/ui/evolution-mail.ui b/ui/evolution-mail.ui index 7d95d73..322c83b 100644 --- a/ui/evolution-mail.ui +++ b/ui/evolution-mail.ui @@ -19,6 +19,7 @@ <placeholder name='view-custom-menus'> <menu action='mail-preview-menu'> <menuitem action='mail-preview'/> + <menuitem action='mail-attachment-bar'/> <separator/> <menuitem action='mail-view-classic'/> <menuitem action='mail-view-vertical'/> diff --git a/web-extensions/Makefile.am b/web-extensions/Makefile.am new file mode 100644 index 0000000..07854c2 --- /dev/null +++ b/web-extensions/Makefile.am @@ -0,0 +1,44 @@ +webextensions_LTLIBRARIES = libewebextension.la libedomutils.la + +libedomutils_la_SOURCES = \ + e-dom-utils.h \ + e-dom-utils.c + +libedomutils_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) + +libedomutils_la_LIBADD = \ + $(top_builddir)/e-util/libevolution-util.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) + +libewebextension_la_SOURCES = \ + e-web-extension.h \ + e-web-extension-names.h \ + e-dom-utils.h \ + e-web-extension.c \ + e-web-extension-main.c \ + e-dom-utils.c + +libewebextension_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(WEB_EXTENSIONS_CFLAGS) + +libewebextension_la_LIBADD = \ + $(top_builddir)/e-util/libevolution-util.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(WEB_EXTENSIONS_LIBS) + +libewebextension_la_LDFLAGS = \ + -module -avoid-version -no-undefined + +-include $(top_srcdir)/git.mk diff --git a/web-extensions/e-dom-utils.c b/web-extensions/e-dom-utils.c new file mode 100644 index 0000000..f671f77 --- /dev/null +++ b/web-extensions/e-dom-utils.c @@ -0,0 +1,2022 @@ +/* + * e-dom-utils.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#define WEBKIT_DOM_USE_UNSTABLE_API +#include <webkitdom/WebKitDOMDOMSelection.h> +#include <webkitdom/WebKitDOMDOMWindowUnstable.h> +#include <webkitdom/WebKitDOMHTMLElementUnstable.h> + +#include "e-web-extension.h" +#include "e-web-extension-names.h" + +#include "e-dom-utils.h" + +void +e_dom_utils_replace_local_image_links (WebKitDOMDocument *document) +{ + gint ii, length; + WebKitDOMNodeList *list = NULL; + + list = webkit_dom_document_query_selector_all ( + document, "img[src^=\"file://\"]", NULL); + length = webkit_dom_node_list_get_length (list); + + for (ii = 0; ii < length; ii++) { + gchar *src, *new_src; + WebKitDOMHTMLImageElement *img; + + img = WEBKIT_DOM_HTML_IMAGE_ELEMENT ( + webkit_dom_node_list_item (list, ii)); + src = webkit_dom_html_image_element_get_src (img); + + /* this forms "evo-file://", which can be loaded, + * while "file://" cannot be, due to WebKit policy */ + new_src = g_strconcat ("evo-", src, NULL); + webkit_dom_html_image_element_set_src (img, new_src); + g_free (new_src); + g_free (src); + g_object_unref (img); + } + g_clear_object (&list); + + list = webkit_dom_document_query_selector_all ( + document, "iframe", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMDocument *content_document; + WebKitDOMHTMLIFrameElement *iframe; + + iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT ( + webkit_dom_node_list_item (list, ii)); + + content_document = + webkit_dom_html_iframe_element_get_content_document (iframe); + + if (content_document && WEBKIT_DOM_IS_DOCUMENT (content_document)) + e_dom_utils_replace_local_image_links (content_document); + g_object_unref (iframe); + } + g_clear_object (&list); +} + +gboolean +e_dom_utils_document_has_selection (WebKitDOMDocument *document) +{ + gboolean ret_val = FALSE; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + + if (!document) + return FALSE; + + dom_window = webkit_dom_document_get_default_view (document); + if (!dom_window) + goto out; + + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + if (!WEBKIT_DOM_IS_DOM_SELECTION (dom_selection)) + goto out; + + if (webkit_dom_dom_selection_get_is_collapsed (dom_selection)) + goto out; + + ret_val = TRUE; + out: + g_clear_object (&dom_window); + g_clear_object (&dom_selection); + + if (!ret_val) { + WebKitDOMHTMLCollection *frames = NULL; + gulong ii, length; + + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + WebKitDOMDocument *content_document; + + node = webkit_dom_html_collection_item (frames, ii); + content_document = webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (node)); + + if ((ret_val = e_dom_utils_document_has_selection (content_document))) { + g_object_unref (node); + break; + } + + g_object_unref (node); + } + + g_clear_object (&frames); + } + + return ret_val; +} + + +gchar * +e_dom_utils_get_document_content_html (WebKitDOMDocument *document) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_get_document_element (document); + + return webkit_dom_element_get_outer_html (element); +} + +static gboolean +element_is_in_pre_tag (WebKitDOMNode *node) +{ + WebKitDOMElement *element; + + if (!node) + return FALSE; + + while (element = webkit_dom_node_get_parent_element (node), element) { + node = WEBKIT_DOM_NODE (element); + + if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (element)) { + return TRUE; + } else if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) { + break; + } + } + + return FALSE; +} + +static gchar * +get_frame_selection_html (WebKitDOMElement *iframe) +{ + WebKitDOMDocument *content_document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMHTMLCollection *frames = NULL; + gulong ii, length; + + content_document = webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)); + + if (!content_document) + return NULL; + + dom_window = webkit_dom_document_get_default_view (content_document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) { + WebKitDOMRange *range = NULL; + WebKitDOMElement *element; + WebKitDOMDocumentFragment *fragment; + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + if (range != NULL) { + gchar *inner_html; + WebKitDOMNode *node; + + fragment = webkit_dom_range_clone_contents ( + range, NULL); + + element = webkit_dom_document_create_element ( + content_document, "DIV", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (fragment), NULL); + + inner_html = webkit_dom_element_get_inner_html (element); + + node = webkit_dom_range_get_start_container (range, NULL); + if (element_is_in_pre_tag (node)) { + gchar *tmp = inner_html; + inner_html = g_strconcat ("<pre>", tmp, "</pre>", NULL); + g_free (tmp); + } + + g_clear_object (&range); + g_clear_object (&dom_selection); + return inner_html; + } + } + + g_clear_object (&dom_selection); + + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (content_document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + gchar *text; + + node = webkit_dom_html_collection_item (frames, ii); + + text = get_frame_selection_html ( + WEBKIT_DOM_ELEMENT (node)); + + g_object_unref (node); + if (text != NULL) { + g_clear_object (&frames); + return text; + } + } + + g_clear_object (&frames); + + return NULL; +} + +gchar * +e_dom_utils_get_selection_content_html (WebKitDOMDocument *document) +{ + WebKitDOMHTMLCollection *frames = NULL; + gulong ii, length; + + if (!e_dom_utils_document_has_selection (document)) + return NULL; + + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + + for (ii = 0; ii < length; ii++) { + gchar *text; + WebKitDOMNode *node; + + node = webkit_dom_html_collection_item (frames, ii); + + text = get_frame_selection_html ( + WEBKIT_DOM_ELEMENT (node)); + + g_object_unref (node); + if (text != NULL) { + g_clear_object (&frames); + return text; + } + } + + g_clear_object (&frames); + return NULL; +} + +static gchar * +get_frame_selection_content_text (WebKitDOMElement *iframe) +{ + WebKitDOMDocument *content_document; + WebKitDOMDOMWindow *dom_window = NULL; + WebKitDOMDOMSelection *dom_selection = NULL; + WebKitDOMHTMLCollection *frames = NULL; + gulong ii, length; + + content_document = webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)); + + if (!content_document) + return NULL; + + dom_window = webkit_dom_document_get_default_view (content_document); + dom_selection = webkit_dom_dom_window_get_selection (dom_window); + g_clear_object (&dom_window); + if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) { + WebKitDOMRange *range = NULL; + gchar *text = NULL; + + range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL); + if (range) + text = webkit_dom_range_to_string (range, NULL); + g_clear_object (&range); + g_clear_object (&dom_selection); + return text; + } + g_clear_object (&dom_selection); + + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (content_document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + gchar *text; + + node = webkit_dom_html_collection_item (frames, ii); + + text = get_frame_selection_content_text ( + WEBKIT_DOM_ELEMENT (node)); + + g_object_unref (node); + if (text != NULL) { + g_clear_object (&frames); + return text; + } + } + + g_clear_object (&frames); + return NULL; +} + +gchar * +e_dom_utils_get_selection_content_text (WebKitDOMDocument *document) +{ + WebKitDOMHTMLCollection *frames = NULL; + gulong ii, length; + + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + + for (ii = 0; ii < length; ii++) { + gchar *text; + WebKitDOMNode *node; + + node = webkit_dom_html_collection_item (frames, ii); + + text = get_frame_selection_content_text ( + WEBKIT_DOM_ELEMENT (node)); + + g_object_unref (node); + if (text != NULL) { + g_clear_object (&frames); + return text; + } + } + + g_clear_object (&frames); + return NULL; +} + +void +e_dom_utils_create_and_add_css_style_sheet (WebKitDOMDocument *document, + const gchar *style_sheet_id) +{ + WebKitDOMElement *style_element; + + style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id); + + if (!style_element) { + WebKitDOMText *dom_text; + WebKitDOMHTMLHeadElement *head; + + dom_text = webkit_dom_document_create_text_node (document, ""); + + /* Create new <style> element */ + style_element = webkit_dom_document_create_element (document, "style", NULL); + webkit_dom_element_set_id ( + style_element, + style_sheet_id); + webkit_dom_html_style_element_set_media ( + WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element), + "screen"); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (style_element), + /* WebKit hack - we have to insert empty TextNode into style element */ + WEBKIT_DOM_NODE (dom_text), + NULL); + + head = webkit_dom_document_get_head (document); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (head), + WEBKIT_DOM_NODE (style_element), + NULL); + + g_object_unref (head); + g_object_unref (dom_text); + g_object_unref (style_element); + } +} + +static void +add_css_rule_into_style_sheet (WebKitDOMDocument *document, + const gchar *style_sheet_id, + const gchar *selector, + const gchar *style) +{ + WebKitDOMElement *style_element; + WebKitDOMStyleSheet *sheet = NULL; + WebKitDOMCSSRuleList *rules_list = NULL; + gint length, ii, selector_length; + gboolean removed = FALSE; + + g_return_if_fail (selector != NULL); + + selector_length = strlen (selector); + style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id); + + if (!style_element) { + e_dom_utils_create_and_add_css_style_sheet (document, style_sheet_id); + style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id); + } + + /* Get sheet that is associated with style element */ + sheet = webkit_dom_html_style_element_get_sheet (WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element)); + + rules_list = webkit_dom_css_style_sheet_get_css_rules (WEBKIT_DOM_CSS_STYLE_SHEET (sheet)); + length = webkit_dom_css_rule_list_get_length (rules_list); + + /* Check if rule exists */ + for (ii = 0; ii < length && !removed; ii++) { + WebKitDOMCSSRule *rule; + gchar *rule_text = NULL; + + rule = webkit_dom_css_rule_list_item (rules_list, ii); + + g_return_if_fail (WEBKIT_DOM_IS_CSS_RULE (rule)); + + rule_text = webkit_dom_css_rule_get_css_text (rule); + + /* Find the start of the style => end of the selector */ + if (rule_text && selector && g_str_has_prefix (rule_text, selector) && + rule_text[selector_length] == ' ' && rule_text[selector_length + 1] == '{') { + /* If exists remove it */ + webkit_dom_css_style_sheet_remove_rule ( + WEBKIT_DOM_CSS_STYLE_SHEET (sheet), + ii, NULL); + length--; + removed = TRUE; + } + + g_free (rule_text); + g_object_unref (rule); + } + + g_clear_object (&rules_list); + + /* Insert the rule at the end, so it will override previously inserted */ + webkit_dom_css_style_sheet_add_rule ( + WEBKIT_DOM_CSS_STYLE_SHEET (sheet), selector, style, length, NULL); + + g_clear_object (&sheet); + g_object_unref (style_element); +} + +static void +add_css_rule_into_style_sheet_recursive (WebKitDOMDocument *document, + const gchar *style_sheet_id, + const gchar *selector, + const gchar *style) +{ + WebKitDOMHTMLCollection *frames = NULL; + gint ii, length; + + /* Add rule to document */ + add_css_rule_into_style_sheet ( + document, + style_sheet_id, + selector, + style); + + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + + /* Add rules to every sub document */ + for (ii = 0; ii < length; ii++) { + WebKitDOMDocument *content_document = NULL; + WebKitDOMNode *node; + + node = webkit_dom_html_collection_item (frames, ii); + content_document = + webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (node)); + + if (!content_document) + continue; + + add_css_rule_into_style_sheet_recursive ( + content_document, + style_sheet_id, + selector, + style); + g_object_unref (node); + } + g_clear_object (&frames); +} + +void +e_dom_utils_add_css_rule_into_style_sheet (WebKitDOMDocument *document, + const gchar *style_sheet_id, + const gchar *selector, + const gchar *style) +{ + g_return_if_fail (style_sheet_id && *style_sheet_id); + g_return_if_fail (selector && *selector); + g_return_if_fail (style && *style); + + add_css_rule_into_style_sheet_recursive ( + document, + style_sheet_id, + selector, + style); +} + +static void +collapse_contacts_list (WebKitDOMEventTarget *event_target, + WebKitDOMEvent *event, + gpointer user_data) +{ + WebKitDOMDocument *document; + WebKitDOMElement *list; + gchar *id, *list_id; + gchar *imagesdir, *src; + gboolean hidden; + + document = user_data; + id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (event_target)); + + if (!id) + return; + + list_id = g_strconcat ("list-", id, NULL); + list = webkit_dom_document_get_element_by_id (document, list_id); + g_free (id); + g_free (list_id); + + if (list == NULL) + return; + + imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); + hidden = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (list)); + + if (hidden) + src = g_strdup_printf ("evo-file://%s/minus.png", imagesdir); + else + src = g_strdup_printf ("evo-file://%s/plus.png", imagesdir); + + webkit_dom_html_element_set_hidden ( + WEBKIT_DOM_HTML_ELEMENT (list), !hidden); + webkit_dom_html_image_element_set_src ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (event_target), src); + + g_free (src); + g_free (imagesdir); +} + +static void +toggle_headers_visibility (WebKitDOMElement *button, + WebKitDOMEvent *event, + WebKitDOMDocument *document) +{ + WebKitDOMElement *short_headers = NULL, *full_headers = NULL; + WebKitDOMCSSStyleDeclaration *css_short = NULL, *css_full = NULL; + gboolean expanded; + const gchar *path; + gchar *css_value; + + short_headers = webkit_dom_document_get_element_by_id ( + document, "__evo-short-headers"); + if (short_headers == NULL) + return; + + css_short = webkit_dom_element_get_style (short_headers); + + full_headers = webkit_dom_document_get_element_by_id ( + document, "__evo-full-headers"); + if (full_headers == NULL) + goto clean; + + css_full = webkit_dom_element_get_style (full_headers); + css_value = webkit_dom_css_style_declaration_get_property_value ( + css_full, "display"); + expanded = (g_strcmp0 (css_value, "table") == 0); + g_free (css_value); + + webkit_dom_css_style_declaration_set_property ( + css_full, "display", + expanded ? "none" : "table", "", NULL); + webkit_dom_css_style_declaration_set_property ( + css_short, "display", + expanded ? "table" : "none", "", NULL); + + if (expanded) + path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png"; + else + path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png"; + + webkit_dom_html_image_element_set_src ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path); + clean: + g_clear_object (&short_headers); + g_clear_object (&css_short); + g_clear_object (&full_headers); + g_clear_object (&css_full); +} + +static void +toggle_address_visibility (WebKitDOMElement *button, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + WebKitDOMElement *full_addr = NULL, *ellipsis = NULL; + WebKitDOMElement *parent = NULL, *bold = NULL; + WebKitDOMCSSStyleDeclaration *css_full = NULL, *css_ellipsis = NULL; + const gchar *path; + gchar *property_value; + gboolean expanded; + GError *error = NULL; + + /* <b> element */ + bold = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (button)); + /* <td> element */ + parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (bold)); + g_object_unref (bold); + + full_addr = webkit_dom_element_query_selector (parent, "#__evo-moreaddr", NULL); + + if (!full_addr) + goto clean; + + css_full = webkit_dom_element_get_style (full_addr); + + ellipsis = webkit_dom_element_query_selector (parent, "#__evo-moreaddr-ellipsis", NULL); + + if (!ellipsis) + goto clean; + + css_ellipsis = webkit_dom_element_get_style (ellipsis); + + property_value = webkit_dom_css_style_declaration_get_property_value (css_full, "display"); + expanded = g_strcmp0 (property_value, "inline") == 0; + g_free (property_value); + + webkit_dom_css_style_declaration_set_property ( + css_full, "display", (expanded ? "none" : "inline"), "", NULL); + webkit_dom_css_style_declaration_set_property ( + css_ellipsis, "display", (expanded ? "inline" : "none"), "", NULL); + + if (expanded) + path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png"; + else + path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png"; + + if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (button)) { + WebKitDOMElement *element; + + element = webkit_dom_element_query_selector (parent, "#__evo-moreaddr-img", NULL); + if (!element) + goto clean; + + webkit_dom_html_image_element_set_src (WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), path); + + g_object_unref (element); + } else + webkit_dom_html_image_element_set_src (WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path); + + g_dbus_connection_emit_signal ( + connection, + NULL, + E_WEB_EXTENSION_OBJECT_PATH, + E_WEB_EXTENSION_INTERFACE, + "HeadersCollapsed", + g_variant_new ("(b)", expanded), + &error); + + if (error) { + g_warning ("Error emitting signal HeadersCollapsed: %s\n", error->message); + g_error_free (error); + } + + clean: + g_clear_object (&css_full); + g_clear_object (&css_ellipsis); + g_clear_object (&full_addr); + g_clear_object (&ellipsis); + g_clear_object (&parent); +} + +static void +e_dom_utils_bind_dom (WebKitDOMDocument *document, + const gchar *selector, + const gchar *event, + gpointer callback, + gpointer user_data) +{ + WebKitDOMNodeList *nodes = NULL; + gulong ii, length; + + nodes = webkit_dom_document_query_selector_all ( + document, selector, NULL); + + length = webkit_dom_node_list_get_length (nodes); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (nodes, ii); + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (node), event, + G_CALLBACK (callback), FALSE, user_data); + } + g_clear_object (&nodes); +} + +static void +e_dom_utils_bind_elements_recursively (WebKitDOMDocument *document, + const gchar *selector, + const gchar *event, + gpointer callback, + gpointer user_data) +{ + WebKitDOMNodeList *nodes = NULL; + WebKitDOMHTMLCollection *frames = NULL; + gulong ii, length; + + nodes = webkit_dom_document_query_selector_all ( + document, selector, NULL); + + length = webkit_dom_node_list_get_length (nodes); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (nodes, ii); + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (node), event, + G_CALLBACK (callback), FALSE, user_data); + } + g_clear_object (&nodes); + + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + + /* Add rules to every sub document */ + for (ii = 0; ii < length; ii++) { + WebKitDOMDocument *content_document = NULL; + WebKitDOMNode *node; + + node = webkit_dom_html_collection_item (frames, ii); + content_document = + webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (node)); + + if (!content_document) + continue; + + e_dom_utils_bind_elements_recursively ( + content_document, + selector, + event, + callback, + user_data); + } + g_clear_object (&frames); +} + +static void +element_focus_cb (WebKitDOMElement *element, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + g_dbus_connection_call ( + connection, + E_WEB_EXTENSION_SERVICE_NAME, + E_WEB_EXTENSION_OBJECT_PATH, + E_WEB_EXTENSION_INTERFACE, + "Set", + g_variant_new ( + "(ssv)", + E_WEB_EXTENSION_INTERFACE, + "NeedInput", + g_variant_new_boolean (TRUE)), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + +} + +static void +element_blur_cb (WebKitDOMElement *element, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + g_dbus_connection_call ( + connection, + E_WEB_EXTENSION_SERVICE_NAME, + E_WEB_EXTENSION_OBJECT_PATH, + E_WEB_EXTENSION_INTERFACE, + "Set", + g_variant_new ( + "(ssv)", + E_WEB_EXTENSION_INTERFACE, + "NeedInput", + g_variant_new_boolean (FALSE)), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + + +void +e_dom_utils_bind_focus_on_elements (WebKitDOMDocument *document, + GDBusConnection *connection) +{ + const gchar *elements = "input, textarea, select, button, label"; + + e_dom_utils_bind_elements_recursively ( + document, + elements, + "focus", + element_focus_cb, + connection); + + e_dom_utils_bind_elements_recursively ( + document, + elements, + "blur", + element_blur_cb, + connection); +} + +void +e_dom_utils_e_mail_display_bind_dom (WebKitDOMDocument *document, + GDBusConnection *connection) +{ + e_dom_utils_bind_dom ( + document, + "#__evo-collapse-headers-img", + "click", + toggle_headers_visibility, + document); + + e_dom_utils_bind_dom ( + document, + "*[id^=__evo-moreaddr-]", + "click", + toggle_address_visibility, + connection); +} + +void +e_dom_utils_eab_contact_formatter_bind_dom (WebKitDOMDocument *document) +{ + e_dom_utils_bind_dom ( + document, + "._evo_collapse_button", + "click", + collapse_contacts_list, + document); +} + +/* ! This function can be called only from WK2 web-extension ! */ +WebKitDOMElement * +e_dom_utils_find_element_by_selector (WebKitDOMDocument *document, + const gchar *selector) +{ + WebKitDOMHTMLCollection *frames = NULL; + WebKitDOMElement *element; + gulong ii, length; + + /* Try to look up the element in this DOM document */ + element = webkit_dom_document_query_selector (document, selector, NULL); + if (element != NULL) + return element; + + /* If the element is not here then recursively scan all frames */ + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + for (ii = 0; ii < length; ii++) { + WebKitDOMHTMLIFrameElement *iframe; + WebKitDOMDocument *content_document; + + iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT ( + webkit_dom_html_collection_item (frames, ii)); + + content_document = webkit_dom_html_iframe_element_get_content_document (iframe); + if (!content_document) + continue; + + element = e_dom_utils_find_element_by_id (content_document, selector); + + g_object_unref (iframe); + if (element != NULL) + break; + } + + g_clear_object (&frames); + return element; +} + +/* ! This function can be called only from WK2 web-extension ! */ +WebKitDOMElement * +e_dom_utils_find_element_by_id (WebKitDOMDocument *document, + const gchar *id) +{ + WebKitDOMHTMLCollection *frames = NULL; + WebKitDOMElement *element; + gulong ii, length; + + /* Try to look up the element in this DOM document */ + element = webkit_dom_document_get_element_by_id (document, id); + if (element != NULL) + return element; + + /* If the element is not here then recursively scan all frames */ + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + for (ii = 0; ii < length; ii++) { + WebKitDOMHTMLIFrameElement *iframe; + WebKitDOMDocument *content_document; + + iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT ( + webkit_dom_html_collection_item (frames, ii)); + + content_document = webkit_dom_html_iframe_element_get_content_document (iframe); + if (!content_document) + continue; + + element = e_dom_utils_find_element_by_id (content_document, id); + + g_object_unref (iframe); + if (element != NULL) + break; + } + + g_clear_object (&frames); + return element; +} + +gboolean +e_dom_utils_element_exists (WebKitDOMDocument *document, + const gchar *element_id) +{ + WebKitDOMElement *element; + + element = e_dom_utils_find_element_by_id (document, element_id); + + return element != NULL; +} + +gchar * +e_dom_utils_get_active_element_name (WebKitDOMDocument *document) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_get_active_element (document); + + if (!element) + return NULL; + + while (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) { + WebKitDOMDocument *content_document; + + content_document = + webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (element)); + + if (!content_document) + break; + + element = webkit_dom_document_get_active_element (content_document); + } + + return webkit_dom_node_get_local_name (WEBKIT_DOM_NODE (element)); +} + +void +e_dom_utils_e_mail_part_headers_bind_dom_element (WebKitDOMDocument *document, + const gchar *element_id) +{ + WebKitDOMDocument *element_document; + WebKitDOMElement *element; + WebKitDOMElement *photo; + gchar *addr; + + element = e_dom_utils_find_element_by_id (document, element_id); + if (!element) + return; + + element_document = webkit_dom_node_get_owner_document ( + WEBKIT_DOM_NODE (element)); + photo = webkit_dom_document_get_element_by_id ( + element_document, "__evo-contact-photo"); + + /* Contact photos disabled, the <img> tag is not there. */ + if (!photo) + return; + + addr = webkit_dom_element_get_attribute (photo, "data-mailaddr"); + if (addr) { + gchar *uri; + + uri = g_strdup_printf ("mail://contact-photo?mailaddr=%s", addr); + + webkit_dom_html_image_element_set_src ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (photo), uri); + + g_free (uri); + } + + g_free (addr); +} + +void +e_dom_utils_element_set_inner_html (WebKitDOMDocument *document, + const gchar *element_id, + const gchar *inner_html) +{ + WebKitDOMElement *element; + + element = e_dom_utils_find_element_by_id (document, element_id); + + if (!element) + return; + + webkit_dom_element_set_inner_html (element, inner_html, NULL); +} + +void +e_dom_utils_remove_element (WebKitDOMDocument *document, + const gchar *element_id) +{ + WebKitDOMElement *element; + + element = e_dom_utils_find_element_by_id (document, element_id); + + if (!element) + return; + + webkit_dom_node_remove_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + WEBKIT_DOM_NODE (element), + NULL); +} + +void +e_dom_utils_element_remove_child_nodes (WebKitDOMDocument *document, + const gchar *element_id) +{ + WebKitDOMNode *node; + WebKitDOMElement *element; + + element = e_dom_utils_find_element_by_id (document, element_id); + if (!element) + return; + + node = WEBKIT_DOM_NODE (element); + + if (!node) + return; + + while (webkit_dom_node_has_child_nodes (node)) { + webkit_dom_node_remove_child ( + node, + webkit_dom_node_get_last_child (node), + NULL); + } +} + +void +e_dom_utils_hide_element (WebKitDOMDocument *document, + const gchar *element_id, + gboolean hide) +{ + WebKitDOMElement *element; + + element = e_dom_utils_find_element_by_id (document, element_id); + + if (!element) + return; + + webkit_dom_html_element_set_hidden ( + WEBKIT_DOM_HTML_ELEMENT (element), hide); +} + +gboolean +e_dom_utils_element_is_hidden (WebKitDOMDocument *document, + const gchar *element_id) +{ + WebKitDOMElement *element; + + element = e_dom_utils_find_element_by_id (document, element_id); + + if (!element) + return FALSE; + + return webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (element)); +} + +static void +get_total_offsets (WebKitDOMElement *element, + glong *left, + glong *top) +{ + WebKitDOMElement *offset_parent; + + if (left) + *left = 0; + + if (top) + *top = 0; + + offset_parent = element; + do { + if (left) { + *left += webkit_dom_element_get_offset_left (offset_parent); + *left -= webkit_dom_element_get_scroll_left (offset_parent); + } + if (top) { + *top += webkit_dom_element_get_offset_top (offset_parent); + *top -= webkit_dom_element_get_scroll_top (offset_parent); + } + offset_parent = webkit_dom_element_get_offset_parent (offset_parent); + } while (offset_parent); +} + +static WebKitDOMElement * +find_element_from_point (WebKitDOMDocument *document, + gint32 x, + gint32 y, + WebKitDOMElement *element_on_point) +{ + WebKitDOMDocument *content_document; + WebKitDOMElement *element; + + if (!element_on_point) + element = webkit_dom_document_element_from_point (document, x, y); + else { + glong left, top; + get_total_offsets (element_on_point, &left, &top); + + element = webkit_dom_document_element_from_point ( + document, x - left, y - top); + } + + if (!element) + return element_on_point; + else if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) + element_on_point = element; + + if (element_on_point && webkit_dom_node_is_equal_node ( + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (element_on_point))) { + return element_on_point; + } + + if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) + return element_on_point; + + content_document = + webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (element)); + + if (!content_document) + return element_on_point; + + return find_element_from_point (content_document, x, y, element); +} + +/* ! This function can be called only from WK2 web-extension ! */ +WebKitDOMElement * +e_dom_utils_get_element_from_point (WebKitDOMDocument *document, + gint32 x, + gint32 y) +{ + return find_element_from_point (document, x, y, NULL); +} + +/* ! This function can be called only from WK2 web-extension ! */ +WebKitDOMDocument * +e_dom_utils_get_document_from_point (WebKitDOMDocument *document, + gint32 x, + gint32 y) +{ + WebKitDOMElement *element; + + if (x == 0 && y == 0) + element = webkit_dom_document_get_active_element (document); + else + element = find_element_from_point (document, x, y, NULL); + + if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) + return webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (element)); + else + return webkit_dom_node_get_owner_document ( + WEBKIT_DOM_NODE (element)); +} + +/* VCard Inline Module DOM functions */ + +static void +display_mode_toggle_button_cb (WebKitDOMElement *button, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + GError *error = NULL; + gchar *element_id; + + element_id = webkit_dom_element_get_id (button); + + g_dbus_connection_emit_signal ( + connection, + NULL, + E_WEB_EXTENSION_OBJECT_PATH, + E_WEB_EXTENSION_INTERFACE, + "VCardInlineDisplayModeToggled", + g_variant_new ("(s)", element_id ? element_id : ""), + &error); + + if (error) { + g_warning ("Error emitting signal DisplayModeToggled: %s\n", error->message); + g_error_free (error); + } + + g_free (element_id); +} + +static void +save_vcard_button_cb (WebKitDOMElement *button, + WebKitDOMEvent *event, + GDBusConnection *connection) +{ + GError *error = NULL; + gchar *button_value; + + button_value = webkit_dom_html_button_element_get_value ( + WEBKIT_DOM_HTML_BUTTON_ELEMENT (button)); + + g_dbus_connection_emit_signal ( + connection, + NULL, + E_WEB_EXTENSION_OBJECT_PATH, + E_WEB_EXTENSION_INTERFACE, + "VCardInlineSaveButtonPressed", + g_variant_new ("(s)", button_value), + &error); + + if (error) { + g_warning ("Error emitting signal SaveVCardButtonPressed: %s\n", error->message); + g_error_free (error); + } + + g_free (button_value); +} + +void +e_dom_utils_module_vcard_inline_bind_dom (WebKitDOMDocument *document, + const gchar *element_id, + GDBusConnection *connection) +{ + WebKitDOMElement *element; + WebKitDOMDocument *element_document; + gchar *selector; + + element = e_dom_utils_find_element_by_id (document, element_id); + if (!element) + return; + + element_document = webkit_dom_node_get_owner_document ( + WEBKIT_DOM_NODE (element)); + + selector = g_strconcat ("button[id='", element_id, "']", NULL); + e_dom_utils_bind_dom ( + element_document, + selector, + "click", + display_mode_toggle_button_cb, + connection); + g_free (selector); + + selector = g_strconcat ("button[value='", element_id, "']", NULL); + e_dom_utils_bind_dom ( + element_document, + selector, + "click", + save_vcard_button_cb, + connection); + g_free (selector); + + e_dom_utils_eab_contact_formatter_bind_dom (element_document); +} + +void +e_dom_utils_module_vcard_inline_update_button (WebKitDOMDocument *document, + const gchar *button_id, + const gchar *html_label, + const gchar *access_key) +{ + WebKitDOMElement *element; + gchar *selector; + + selector = g_strconcat ("button[id='", button_id, "']", NULL); + element = e_dom_utils_find_element_by_selector (document, selector); + g_free (selector); + + if (!element) + return; + + webkit_dom_element_set_inner_html (element, html_label, NULL); + + if (access_key) { + webkit_dom_html_element_set_access_key ( + WEBKIT_DOM_HTML_ELEMENT (element), access_key); + } +} + +void +e_dom_utils_module_vcard_inline_set_iframe_src (WebKitDOMDocument *document, + const gchar *button_id, + const gchar *src) +{ + WebKitDOMElement *element, *parent, *iframe; + gchar *selector; + + selector = g_strconcat ("button[id='", button_id, "']", NULL); + element = e_dom_utils_find_element_by_selector (document, selector); + g_free (selector); + + parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element)); + if (!parent) + return; + + iframe = webkit_dom_element_query_selector (parent, "iframe", NULL); + if (!iframe) + return; + + webkit_dom_html_iframe_element_set_src ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe), src); +} + +/** + * e_html_editor_dom_node_find_parent_element: + * @node: Start node + * @tagname: Tag name of element to search + * + * Recursively searches for first occurance of element with given @tagname + * that is parent of given @node. + * + * Returns: A #WebKitDOMElement with @tagname representing parent of @node or + * @NULL when @node has no parent with given @tagname. When @node matches @tagname, + * then the @node is returned. + */ +WebKitDOMElement * +dom_node_find_parent_element (WebKitDOMNode *node, + const gchar *tagname) +{ + WebKitDOMNode *tmp_node = node; + gint taglen = strlen (tagname); + + while (tmp_node) { + if (WEBKIT_DOM_IS_ELEMENT (tmp_node)) { + gchar *node_tagname; + + node_tagname = webkit_dom_element_get_tag_name ( + WEBKIT_DOM_ELEMENT (tmp_node)); + + if (node_tagname && + (strlen (node_tagname) == taglen) && + (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) { + g_free (node_tagname); + return WEBKIT_DOM_ELEMENT (tmp_node); + } + + g_free (node_tagname); + } + + tmp_node = webkit_dom_node_get_parent_node (tmp_node); + } + + return NULL; +} + +/** + * e_html_editor_dom_node_find_child_element: + * @node: Start node + * @tagname: Tag name of element to search. + * + * Recursively searches for first occurrence of element with given @tagname that + * is a child of @node. + * + * Returns: A #WebKitDOMElement with @tagname representing a child of @node or + * @NULL when @node has no child with given @tagname. When @node matches @tagname, + * then the @node is returned. + */ +WebKitDOMElement * +dom_node_find_child_element (WebKitDOMNode *node, + const gchar *tagname) +{ + WebKitDOMNode *start_node = node; + gint taglen = strlen (tagname); + + do { + if (WEBKIT_DOM_IS_ELEMENT (node)) { + gchar *node_tagname; + + node_tagname = webkit_dom_element_get_tag_name ( + WEBKIT_DOM_ELEMENT (node)); + + if (node_tagname && + (strlen (node_tagname) == taglen) && + (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) { + g_free (node_tagname); + return WEBKIT_DOM_ELEMENT (node); + } + + g_free (node_tagname); + } + + if (webkit_dom_node_has_child_nodes (node)) { + node = webkit_dom_node_get_first_child (node); + } else if (webkit_dom_node_get_next_sibling (node)) { + node = webkit_dom_node_get_next_sibling (node); + } else { + node = webkit_dom_node_get_parent_node (node); + } + } while (!webkit_dom_node_is_same_node (node, start_node)); + + return NULL; +} + +gboolean +element_has_id (WebKitDOMElement *element, + const gchar* id) +{ + gchar *element_id; + + if (!element) + return FALSE; + + if (!WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + element_id = webkit_dom_element_get_id (element); + + if (element_id && g_ascii_strcasecmp (element_id, id) == 0) { + g_free (element_id); + return TRUE; + } + g_free (element_id); + + return FALSE; +} + +gboolean +element_has_tag (WebKitDOMElement *element, + const gchar* tag) +{ + gchar *element_tag; + + if (!WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + element_tag = webkit_dom_node_get_local_name (WEBKIT_DOM_NODE (element)); + + if (g_ascii_strcasecmp (element_tag, tag) != 0) { + g_free (element_tag); + return FALSE; + } + g_free (element_tag); + + return TRUE; +} + +gboolean +element_has_class (WebKitDOMElement *element, + const gchar* class) +{ + gchar *element_class; + + if (!element) + return FALSE; + + if (!WEBKIT_DOM_IS_ELEMENT (element)) + return FALSE; + + element_class = webkit_dom_element_get_class_name (element); + + if (element_class && g_strstr_len (element_class, -1, class)) { + g_free (element_class); + return TRUE; + } + g_free (element_class); + + return FALSE; +} + +void +element_add_class (WebKitDOMElement *element, + const gchar* class) +{ + gchar *element_class; + gchar *new_class; + + if (!WEBKIT_DOM_IS_ELEMENT (element)) + return; + + if (element_has_class (element, class)) + return; + + element_class = webkit_dom_element_get_class_name (element); + + if (!element_class) + new_class = g_strdup (class); + else + new_class = g_strconcat (element_class, " ", class, NULL); + + webkit_dom_element_set_class_name (element, new_class); + + g_free (element_class); + g_free (new_class); +} + +void +element_remove_class (WebKitDOMElement *element, + const gchar* class) +{ + gchar *element_class, *final_class; + GRegex *regex; + gchar *pattern = NULL; + + if (!WEBKIT_DOM_IS_ELEMENT (element)) + return; + + if (!element_has_class (element, class)) + return; + + element_class = webkit_dom_element_get_class_name (element); + + pattern = g_strconcat ("[\\s]*", class, "[\\s]*", NULL); + regex = g_regex_new (pattern, 0, 0, NULL); + final_class = g_regex_replace (regex, element_class, -1, 0, " ", 0, NULL); + + if (g_strcmp0 (final_class, " ") != 0) + webkit_dom_element_set_class_name (element, final_class); + else + webkit_dom_element_remove_attribute (element, "class"); + + g_free (element_class); + g_free (final_class); + g_free (pattern); + g_regex_unref (regex); +} + +void +element_rename_attribute (WebKitDOMElement *element, + const gchar *from, + const gchar *to) +{ + gchar *value; + + if (!webkit_dom_element_has_attribute (element, from)) + return; + + value = webkit_dom_element_get_attribute (element, from); + webkit_dom_element_set_attribute (element, to, (value && *value) ? value : "", NULL); + webkit_dom_element_remove_attribute (element, from); + g_free (value); +} + +void +remove_node (WebKitDOMNode *node) +{ + WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); + + /* Check if the parent exists, if so it means that the node is still + * in the DOM or at least the parent is. If it doesn't exists it is not + * in the DOM and we can free it. */ + if (parent) + webkit_dom_node_remove_child (parent, node, NULL); + else + g_object_unref (node); +} + +void +remove_node_if_empty (WebKitDOMNode *node) +{ + WebKitDOMNode *child; + + if (!WEBKIT_DOM_IS_NODE (node)) + return; + + if ((child = webkit_dom_node_get_first_child (node))) { + WebKitDOMNode *prev_sibling, *next_sibling; + + prev_sibling = webkit_dom_node_get_previous_sibling (child); + next_sibling = webkit_dom_node_get_next_sibling (child); + /* Empty or BR as sibling, but no sibling after it. */ + if (!webkit_dom_node_get_first_child (child) && + !WEBKIT_DOM_IS_TEXT (child) && + (!prev_sibling || + (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling) && + !webkit_dom_node_get_previous_sibling (prev_sibling))) && + (!next_sibling || + (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling) && + !webkit_dom_node_get_next_sibling (next_sibling)))) { + + remove_node (node); + } else { + gchar *text_content; + + text_content = webkit_dom_node_get_text_content (node); + if (!text_content) + remove_node (node); + + if (text_content && !*text_content) + remove_node (node); + + g_free (text_content); + } + } else + remove_node (node); +} + +WebKitDOMNode * +split_list_into_two (WebKitDOMNode *item, + gint level) +{ + gint current_level = 1; + WebKitDOMDocument *document; + WebKitDOMDocumentFragment *fragment; + WebKitDOMNode *parent, *prev_parent = NULL, *tmp; + + document = webkit_dom_node_get_owner_document (item); + fragment = webkit_dom_document_create_document_fragment (document); + + tmp = item; + parent = webkit_dom_node_get_parent_node (item); + while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + WebKitDOMNode *clone, *first_child, *insert_before = NULL, *sibling; + + first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + clone = webkit_dom_node_clone_node_with_error (parent, FALSE, NULL); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (fragment), clone, first_child, NULL); + + if (first_child) + insert_before = webkit_dom_node_get_first_child (first_child); + + while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child))) + webkit_dom_node_insert_before (first_child, sibling, insert_before, NULL); + + while (tmp && (sibling = webkit_dom_node_get_next_sibling (tmp))) + webkit_dom_node_append_child (clone, sibling, NULL); + + if (tmp) + webkit_dom_node_insert_before ( + clone, tmp, webkit_dom_node_get_first_child (clone), NULL); + + prev_parent = parent; + tmp = webkit_dom_node_get_next_sibling (parent); + parent = webkit_dom_node_get_parent_node (parent); + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { + first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)); + insert_before = webkit_dom_node_get_first_child (first_child); + while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child))) { + webkit_dom_node_insert_before ( + first_child, sibling, insert_before, NULL); + } + } + + if (current_level >= level && level >= 0) + break; + + current_level++; + } + + tmp = webkit_dom_node_insert_before ( + parent, + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)), + prev_parent ? webkit_dom_node_get_next_sibling (prev_parent) : NULL, + NULL); + remove_node_if_empty (prev_parent); + + return tmp; +} + +WebKitDOMElement * +dom_create_selection_marker (WebKitDOMDocument *document, + gboolean selection_start_marker) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_create_element ( + document, "SPAN", NULL); + webkit_dom_element_set_id ( + element, + selection_start_marker ? + "-x-evo-selection-start-marker" : + "-x-evo-selection-end-marker"); + + return element; +} + +void +dom_remove_selection_markers (WebKitDOMDocument *document) +{ + WebKitDOMElement *marker; + + marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + if (marker) + remove_node (WEBKIT_DOM_NODE (marker)); + marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + if (marker) + remove_node (WEBKIT_DOM_NODE (marker)); +} + +void +dom_add_selection_markers_into_element_start (WebKitDOMDocument *document, + WebKitDOMElement *element, + WebKitDOMElement **selection_start_marker, + WebKitDOMElement **selection_end_marker) +{ + WebKitDOMElement *marker; + + dom_remove_selection_markers (document); + marker = dom_create_selection_marker (document, FALSE); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (marker), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)), + NULL); + if (selection_end_marker) + *selection_end_marker = marker; + + marker = dom_create_selection_marker (document, TRUE); + webkit_dom_node_insert_before ( + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (marker), + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)), + NULL); + if (selection_start_marker) + *selection_start_marker = marker; +} + +void +dom_add_selection_markers_into_element_end (WebKitDOMDocument *document, + WebKitDOMElement *element, + WebKitDOMElement **selection_start_marker, + WebKitDOMElement **selection_end_marker) +{ + WebKitDOMElement *marker; + + dom_remove_selection_markers (document); + marker = dom_create_selection_marker (document, TRUE); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL); + if (selection_start_marker) + *selection_start_marker = marker; + + marker = dom_create_selection_marker (document, FALSE); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL); + if (selection_end_marker) + *selection_end_marker = marker; +} + +gboolean +node_is_list_or_item (WebKitDOMNode *node) +{ + return node && ( + WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (node) || + WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node) || + WEBKIT_DOM_IS_HTML_LI_ELEMENT (node)); +} + +gboolean +node_is_list (WebKitDOMNode *node) +{ + return node && ( + WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (node) || + WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node)); +} + +/** + * e_html_editor_selection_get_list_format_from_node: + * @node: an #WebKitDOMNode + * + * Returns block format of given list. + * + * Returns: #EContentEditorBlockFormat + */ +EContentEditorBlockFormat +dom_get_list_format_from_node (WebKitDOMNode *node) +{ + EContentEditorBlockFormat format = + E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST; + + if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node)) + return E_CONTENT_EDITOR_BLOCK_FORMAT_NONE; + + if (WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node)) + return format; + + if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (node)) { + gchar *type_value = webkit_dom_element_get_attribute ( + WEBKIT_DOM_ELEMENT (node), "type"); + + if (!type_value) + return E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST; + + if (!*type_value) + format = E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST; + else if (g_ascii_strcasecmp (type_value, "A") == 0) + format = E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA; + else if (g_ascii_strcasecmp (type_value, "I") == 0) + format = E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN; + g_free (type_value); + + return format; + } + + return E_CONTENT_EDITOR_BLOCK_FORMAT_NONE; +} + +void +merge_list_into_list (WebKitDOMNode *from, + WebKitDOMNode *to, + gboolean insert_before) +{ + WebKitDOMNode *item, *insert_before_node; + + if (!(to && from)) + return; + + insert_before_node = webkit_dom_node_get_first_child (to); + while ((item = webkit_dom_node_get_first_child (from)) != NULL) { + if (insert_before) + webkit_dom_node_insert_before ( + to, item, insert_before_node, NULL); + else + webkit_dom_node_append_child (to, item, NULL); + } + + if (!webkit_dom_node_has_child_nodes (from)) + remove_node (from); + +} + +void +merge_lists_if_possible (WebKitDOMNode *list) +{ + EContentEditorBlockFormat format, prev, next; + gint ii, length; + WebKitDOMNode *prev_sibling, *next_sibling; + WebKitDOMNodeList *lists = NULL; + + prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (list)); + next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (list)); + + format = dom_get_list_format_from_node (list), + prev = dom_get_list_format_from_node (prev_sibling); + next = dom_get_list_format_from_node (next_sibling); + + if (format != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) { + if (format == prev && prev != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) + merge_list_into_list (prev_sibling, list, TRUE); + + if (format == next && next != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) + merge_list_into_list (next_sibling, list, FALSE); + } + + lists = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (list), "ol + ol, ul + ul", NULL); + length = webkit_dom_node_list_get_length (lists); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_node_list_item (lists, ii); + merge_lists_if_possible (node); + g_object_unref (node); + } + g_clear_object (&lists); +} + +WebKitDOMElement * +get_parent_block_element (WebKitDOMNode *node) +{ + WebKitDOMElement *parent = webkit_dom_node_get_parent_element (node); + + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) + return WEBKIT_DOM_ELEMENT (node); + + while (parent && + !WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent) && + !WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) && + !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) && + !WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (parent) && + !WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (parent) && + !WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent) && + !WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (parent) && + !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent) && + !element_has_tag (parent, "address")) { + parent = webkit_dom_node_get_parent_element ( + WEBKIT_DOM_NODE (parent)); + } + + return parent; +} + +gchar * +dom_get_node_inner_html (WebKitDOMNode *node) +{ + gchar *inner_html; + WebKitDOMDocument *document; + WebKitDOMElement *div; + + document = webkit_dom_node_get_owner_document (node); + div = webkit_dom_document_create_element (document, "div", NULL); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (div), + webkit_dom_node_clone_node_with_error (node, TRUE, NULL), + NULL); + + inner_html = webkit_dom_element_get_inner_html (div); + remove_node (WEBKIT_DOM_NODE (div)); + + return inner_html; +} + +WebKitDOMDocument * +e_dom_utils_find_document_with_uri (WebKitDOMDocument *root_document, + const gchar *find_document_uri) +{ + WebKitDOMDocument *res_document = NULL; + GSList *todo; + + g_return_val_if_fail (WEBKIT_DOM_IS_DOCUMENT (root_document), NULL); + g_return_val_if_fail (find_document_uri != NULL, NULL); + + todo = g_slist_append (NULL, root_document); + + while (todo) { + WebKitDOMDocument *document; + WebKitDOMHTMLCollection *frames = NULL; + gchar *document_uri; + gint ii, length; + + document = todo->data; + todo = g_slist_remove (todo, document); + + document_uri = webkit_dom_document_get_document_uri (document); + if (g_strcmp0 (document_uri, find_document_uri) == 0) { + g_free (document_uri); + res_document = document; + break; + } + + g_free (document_uri); + + frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + length = webkit_dom_html_collection_get_length (frames); + + /* Add rules to every sub document */ + for (ii = 0; ii < length; ii++) { + WebKitDOMDocument *content_document; + WebKitDOMNode *node; + + node = webkit_dom_html_collection_item (frames, ii); + content_document = + webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (node)); + + if (!content_document) + continue; + + todo = g_slist_prepend (todo, content_document); + + g_object_unref (node); + } + + g_clear_object (&frames); + } + + g_slist_free (todo); + + return res_document; +} + +void +dom_element_swap_attributes (WebKitDOMElement *element, + const gchar *from, + const gchar *to) +{ + gchar *value_from, *value_to; + + if (!webkit_dom_element_has_attribute (element, from) || + !webkit_dom_element_has_attribute (element, to)) + return; + + value_from = webkit_dom_element_get_attribute (element, from); + value_to = webkit_dom_element_get_attribute (element, to); + webkit_dom_element_set_attribute (element, to, (value_from && *value_from) ? value_from : "", NULL); + webkit_dom_element_set_attribute (element, from, (value_to && *value_to) ? value_to : "", NULL); + g_free (value_from); + g_free (value_to); +} diff --git a/web-extensions/e-dom-utils.h b/web-extensions/e-dom-utils.h new file mode 100644 index 0000000..3ec289d --- /dev/null +++ b/web-extensions/e-dom-utils.h @@ -0,0 +1,169 @@ +/* + * e-dom-utils.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_DOM_UTILS_H +#define E_DOM_UTILS_H + +#define E_UTIL_INCLUDE_WITHOUT_WEBKIT +#include <e-util/e-util.h> +#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT + +#include <webkitdom/webkitdom.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +void e_dom_utils_replace_local_image_links + (WebKitDOMDocument *document); +gboolean e_dom_utils_document_has_selection + (WebKitDOMDocument *document); +gchar * e_dom_utils_get_document_content_html + (WebKitDOMDocument *document); +gchar * e_dom_utils_get_selection_content_html + (WebKitDOMDocument *document); +gchar * e_dom_utils_get_selection_content_text + (WebKitDOMDocument *document); +void e_dom_utils_create_and_add_css_style_sheet + (WebKitDOMDocument *document, + const gchar *style_sheet_id); +void e_dom_utils_add_css_rule_into_style_sheet + (WebKitDOMDocument *document, + const gchar *style_sheet_id, + const gchar *selector, + const gchar *style); +void e_dom_utils_eab_contact_formatter_bind_dom + (WebKitDOMDocument *document); +void e_dom_utils_bind_focus_on_elements + (WebKitDOMDocument *document, + GDBusConnection *connection); +void e_dom_utils_e_mail_display_bind_dom + (WebKitDOMDocument *document, + GDBusConnection *connection); +WebKitDOMElement * + e_dom_utils_find_element_by_selector + (WebKitDOMDocument *document, + const gchar *selector); +WebKitDOMElement * + e_dom_utils_find_element_by_id (WebKitDOMDocument *document, + const gchar *element_id); +gboolean e_dom_utils_element_exists + (WebKitDOMDocument *document, + const gchar *element_id); +gchar * e_dom_utils_get_active_element_name + (WebKitDOMDocument *document); +void e_dom_utils_e_mail_part_headers_bind_dom_element + (WebKitDOMDocument *document, + const gchar *element_id); +void e_dom_utils_element_set_inner_html + (WebKitDOMDocument *document, + const gchar *element_id, + const gchar *inner_html); +void e_dom_utils_remove_element (WebKitDOMDocument *document, + const gchar *element_id); +void e_dom_utils_element_remove_child_nodes + (WebKitDOMDocument *document, + const gchar *element_id); +void e_dom_utils_hide_element (WebKitDOMDocument *document, + const gchar *element_id, + gboolean hide); +gboolean e_dom_utils_element_is_hidden (WebKitDOMDocument *document, + const gchar *element_id); +WebKitDOMElement * + e_dom_utils_get_element_from_point + (WebKitDOMDocument *document, + gint32 x, + gint32 y); +WebKitDOMDocument * + e_dom_utils_get_document_from_point + (WebKitDOMDocument *document, + gint32 x, + gint32 y); +/* VCard Inline Module DOM functions */ +void e_dom_utils_module_vcard_inline_bind_dom + (WebKitDOMDocument *document, + const gchar *element_id, + GDBusConnection *connection); +void e_dom_utils_module_vcard_inline_update_button + (WebKitDOMDocument *document, + const gchar *button_id, + const gchar *html_label, + const gchar *access_key); +void e_dom_utils_module_vcard_inline_set_iframe_src + (WebKitDOMDocument *document, + const gchar *button_id, + const gchar *src); +WebKitDOMElement * + dom_node_find_parent_element (WebKitDOMNode *node, + const gchar *tagname); +WebKitDOMElement * + dom_node_find_child_element (WebKitDOMNode *node, + const gchar *tagname); +gboolean element_has_id (WebKitDOMElement *element, + const gchar* id); +gboolean element_has_tag (WebKitDOMElement *element, + const gchar* tag); +gboolean element_has_class (WebKitDOMElement *element, + const gchar* class); +void element_add_class (WebKitDOMElement *element, + const gchar* class); +void element_remove_class (WebKitDOMElement *element, + const gchar* class); +void element_rename_attribute (WebKitDOMElement *element, + const gchar *from, + const gchar *to); +void remove_node (WebKitDOMNode *node); +void remove_node_if_empty (WebKitDOMNode *node); +WebKitDOMNode * split_list_into_two (WebKitDOMNode *item, + gint level); +WebKitDOMElement * + dom_create_selection_marker (WebKitDOMDocument *document, + gboolean start); +void dom_add_selection_markers_into_element_start + (WebKitDOMDocument *document, + WebKitDOMElement *element, + WebKitDOMElement **selection_start_marker, + WebKitDOMElement **selection_end_marker); +void dom_add_selection_markers_into_element_end + (WebKitDOMDocument *document, + WebKitDOMElement *element, + WebKitDOMElement **selection_start_marker, + WebKitDOMElement **selection_end_marker); +void dom_remove_selection_markers (WebKitDOMDocument *document); +gboolean node_is_list (WebKitDOMNode *node); +gboolean node_is_list_or_item (WebKitDOMNode *node); +EContentEditorBlockFormat + dom_get_list_format_from_node (WebKitDOMNode *node); +void merge_list_into_list (WebKitDOMNode *from, + WebKitDOMNode *to, + gboolean insert_before); +void merge_lists_if_possible (WebKitDOMNode *list); +WebKitDOMElement * + get_parent_block_element (WebKitDOMNode *node); +gchar * dom_get_node_inner_html (WebKitDOMNode *node); +WebKitDOMDocument * + e_dom_utils_find_document_with_uri + (WebKitDOMDocument *root_document, + const gchar *find_document_uri); +void dom_element_swap_attributes (WebKitDOMElement *element, + const gchar *from, + const gchar *to); + +G_END_DECLS + +#endif /* E_DOM_UTILS_H */ diff --git a/web-extensions/e-web-extension-main.c b/web-extensions/e-web-extension-main.c new file mode 100644 index 0000000..5276734 --- /dev/null +++ b/web-extensions/e-web-extension-main.c @@ -0,0 +1,61 @@ +/* + * e-web-extension-main.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <camel/camel.h> + +#include "e-web-extension.h" +#include "e-web-extension-names.h" + +static void +bus_acquired_cb (GDBusConnection *connection, + const gchar *name, + EWebExtension *extension) +{ + e_web_extension_dbus_register (extension, connection); +} + +/* Forward declaration */ +G_MODULE_EXPORT void webkit_web_extension_initialize (WebKitWebExtension *wk_extension); + +G_MODULE_EXPORT void +webkit_web_extension_initialize (WebKitWebExtension *wk_extension) +{ + EWebExtension *extension; + + camel_debug_init (); + + if (camel_debug ("wex")) + printf ("%s\n", G_STRFUNC); + + extension = e_web_extension_get (); + e_web_extension_initialize (extension, wk_extension); + + g_bus_own_name ( + G_BUS_TYPE_SESSION, + E_WEB_EXTENSION_SERVICE_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + (GBusAcquiredCallback) bus_acquired_cb, + NULL, /* GBusNameAcquiredCallback */ + NULL, /* GBusNameLostCallback */ + g_object_ref (extension), + (GDestroyNotify) g_object_unref); +} diff --git a/web-extensions/e-web-extension-names.h b/web-extensions/e-web-extension-names.h new file mode 100644 index 0000000..eedf271 --- /dev/null +++ b/web-extensions/e-web-extension-names.h @@ -0,0 +1,26 @@ +/* + * e-web-extension-names.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_WEB_EXTENSION_NAMES_H +#define E_WEB_EXTENSION_NAMES_H + +#define E_WEB_EXTENSION_SERVICE_NAME "org.gnome.Evolution.WebExtension" +#define E_WEB_EXTENSION_OBJECT_PATH "/org/gnome/Evolution/WebExtension" +#define E_WEB_EXTENSION_INTERFACE "org.gnome.Evolution.WebExtension" + +#endif /* E_WEB_EXTENSION_NAMES_H */ diff --git a/web-extensions/e-web-extension.c b/web-extensions/e-web-extension.c new file mode 100644 index 0000000..0492077 --- /dev/null +++ b/web-extensions/e-web-extension.c @@ -0,0 +1,999 @@ +/* + * e-web-extension.c + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <gio/gio.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> + +#include <camel/camel.h> +#include <libedataserver/libedataserver.h> + +#include "e-web-extension.h" +#include "e-dom-utils.h" +#include "e-web-extension-names.h" + +#define WEBKIT_DOM_USE_UNSTABLE_API +#include <webkitdom/WebKitDOMDOMWindowUnstable.h> + +#define WEB_EXTENSION_PAGE_ID_KEY "web-extension-page-id" + +#define E_WEB_EXTENSION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_WEB_EXTENSION, EWebExtensionPrivate)) + +struct _EWebExtensionPrivate { + WebKitWebExtension *wk_extension; + + GDBusConnection *dbus_connection; + guint registration_id; + + gboolean initialized; + + gboolean need_input; +}; + +static const char introspection_xml[] = +"<node>" +" <interface name='" E_WEB_EXTENSION_INTERFACE "'>" +" <method name='RegisterElementClicked'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_class' direction='in'/>" +" </method>" +" <signal name='ElementClicked'>" +" <arg type='t' name='page_id' direction='out'/>" +" <arg type='s' name='element_class' direction='out'/>" +" <arg type='s' name='element_value' direction='out'/>" +" <arg type='i' name='position_left' direction='out'/>" +" <arg type='i' name='position_top' direction='out'/>" +" <arg type='i' name='position_width' direction='out'/>" +" <arg type='i' name='position_height' direction='out'/>" +" </signal>" +" <method name='SetElementHidden'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='b' name='hidden' direction='in'/>" +" </method>" +" <method name='SetElementStyleProperty'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='property_name' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" <arg type='s' name='priority' direction='in'/>" +" </method>" +" <method name='SetElementAttribute'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='s' name='namespace_uri' direction='in'/>" +" <arg type='s' name='qualified_name' direction='in'/>" +" <arg type='s' name='value' direction='in'/>" +" </method>" +" <signal name='HeadersCollapsed'>" +" <arg type='b' name='expanded' direction='out'/>" +" </signal>" +" <method name='DocumentHasSelection'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='b' name='has_selection' direction='out'/>" +" </method>" +" <method name='GetDocumentContentHTML'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='html_content' direction='out'/>" +" </method>" +" <method name='GetSelectionContentHTML'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='html_content' direction='out'/>" +" </method>" +" <method name='GetSelectionContentText'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='text_content' direction='out'/>" +" </method>" +" <method name='CreateAndAddCSSStyleSheet'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='style_sheet_id' direction='in'/>" +" </method>" +" <method name='AddCSSRuleIntoStyleSheet'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='style_sheet_id' direction='in'/>" +" <arg type='s' name='selector' direction='in'/>" +" <arg type='s' name='style' direction='in'/>" +" </method>" +" <method name='EABContactFormatterBindDOM'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='EMailDisplayBindDOM'>" +" <arg type='t' name='page_id' direction='in'/>" +" </method>" +" <method name='ElementExists'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" <arg type='b' name='element_exists' direction='out'/>" +" <arg type='t' name='page_id' direction='out'/>" +" </method>" +" <method name='GetActiveElementName'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_name' direction='out'/>" +" </method>" +" <method name='EMailPartHeadersBindDOMElement'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" </method>" +" <signal name='VCardInlineDisplayModeToggled'>" +" <arg type='s' name='button_id' direction='out'/>" +" </signal>" +" <signal name='VCardInlineSaveButtonPressed'>" +" <arg type='s' name='button_value' direction='out'/>" +" </signal>" +" <method name='VCardInlineBindDOM'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='element_id' direction='in'/>" +" </method>" +" <method name='VCardInlineUpdateButton'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='button_id' direction='in'/>" +" <arg type='s' name='html_label' direction='in'/>" +" <arg type='s' name='access_key' direction='in'/>" +" </method>" +" <method name='VCardInlineSetIFrameSrc'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='button_id' direction='in'/>" +" <arg type='s' name='src' direction='in'/>" +" </method>" +" <method name='GetDocumentURIFromPoint'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='i' name='x' direction='in'/>" +" <arg type='i' name='y' direction='in'/>" +" <arg type='s' name='document_uri' direction='out'/>" +" </method>" +" <method name='SetDocumentIFrameSrc'>" +" <arg type='t' name='page_id' direction='in'/>" +" <arg type='s' name='document_uri' direction='in'/>" +" <arg type='s' name='new_iframe_src' direction='in'/>" +" </method>" +" <property type='b' name='NeedInput' access='readwrite'/>" +" </interface>" +"</node>"; + +G_DEFINE_TYPE (EWebExtension, e_web_extension, G_TYPE_OBJECT) + +static WebKitWebPage * +get_webkit_web_page_or_return_dbus_error (GDBusMethodInvocation *invocation, + WebKitWebExtension *web_extension, + guint64 page_id) +{ + WebKitWebPage *web_page = webkit_web_extension_get_page (web_extension, page_id); + if (!web_page) { + g_dbus_method_invocation_return_error ( + invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Invalid page ID: %" G_GUINT64_FORMAT, page_id); + } + return web_page; +} + +static void +element_clicked_cb (WebKitDOMElement *element, + WebKitDOMEvent *event, + gpointer user_data) +{ + EWebExtension *extension = user_data; + WebKitDOMElement *offset_parent; + WebKitDOMDOMWindow *dom_window = NULL; + gchar *attr_class, *attr_value; + const guint64 *ppage_id; + gdouble with_parents_left, with_parents_top; + glong scroll_x = 0, scroll_y = 0; + GError *error = NULL; + + g_return_if_fail (E_IS_WEB_EXTENSION (extension)); + g_return_if_fail (G_IS_OBJECT (element)); + + ppage_id = g_object_get_data (G_OBJECT (element), WEB_EXTENSION_PAGE_ID_KEY); + g_return_if_fail (ppage_id != NULL); + + with_parents_left = webkit_dom_element_get_offset_left (element); + with_parents_top = webkit_dom_element_get_offset_top (element); + + offset_parent = element; + while (offset_parent = webkit_dom_element_get_offset_parent (offset_parent), offset_parent) { + with_parents_left += webkit_dom_element_get_offset_left (offset_parent); + with_parents_top += webkit_dom_element_get_offset_top (offset_parent); + } + + dom_window = webkit_dom_document_get_default_view (webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element))); + if (WEBKIT_DOM_IS_DOM_WINDOW (dom_window)) { + g_object_get (G_OBJECT (dom_window), + "scroll-x", &scroll_x, + "scroll-y", &scroll_y, + NULL); + } + g_clear_object (&dom_window); + + attr_class = webkit_dom_element_get_class_name (element); + attr_value = webkit_dom_element_get_attribute (element, "value"); + + g_dbus_connection_emit_signal ( + extension->priv->dbus_connection, + NULL, + E_WEB_EXTENSION_OBJECT_PATH, + E_WEB_EXTENSION_INTERFACE, + "ElementClicked", + g_variant_new ("(tssiiii)", *ppage_id, attr_class ? attr_class : "", attr_value ? attr_value : "", + (gint) (with_parents_left - scroll_x), + (gint) (with_parents_top - scroll_y), + (gint) webkit_dom_element_get_offset_width (element), + (gint) webkit_dom_element_get_offset_height (element)), + &error); + + if (error) { + g_warning ("Error emitting signal ElementClicked: %s\n", error->message); + g_error_free (error); + } + + g_free (attr_class); + g_free (attr_value); +} + +static void +web_extension_register_element_clicked_in_document (EWebExtension *extension, + guint64 page_id, + WebKitDOMDocument *document, + const gchar *element_class) +{ + WebKitDOMHTMLCollection *collection = NULL; + gulong ii, len; + + g_return_if_fail (E_IS_WEB_EXTENSION (extension)); + g_return_if_fail (WEBKIT_DOM_IS_DOCUMENT (document)); + g_return_if_fail (element_class && *element_class); + + collection = webkit_dom_document_get_elements_by_class_name_as_html_collection (document, element_class); + if (collection) { + len = webkit_dom_html_collection_get_length (collection); + for (ii = 0; ii < len; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_html_collection_item (collection, ii); + if (WEBKIT_DOM_IS_EVENT_TARGET (node)) { + guint64 *ppage_id; + + ppage_id = g_new0 (guint64, 1); + *ppage_id = page_id; + + g_object_set_data_full (G_OBJECT (node), WEB_EXTENSION_PAGE_ID_KEY, ppage_id, g_free); + + /* Remove first, in case there was a listener already (it's when + the page is dynamically filled and not all the elements are + available in time of the first call. */ + webkit_dom_event_target_remove_event_listener ( + WEBKIT_DOM_EVENT_TARGET (node), "click", + G_CALLBACK (element_clicked_cb), FALSE); + + webkit_dom_event_target_add_event_listener ( + WEBKIT_DOM_EVENT_TARGET (node), "click", + G_CALLBACK (element_clicked_cb), FALSE, extension); + } + } + } + g_clear_object (&collection); + + /* Traverse also iframe-s */ + collection = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe"); + if (collection) { + len = webkit_dom_html_collection_get_length (collection); + for (ii = 0; ii < len; ii++) { + WebKitDOMNode *node; + + node = webkit_dom_html_collection_item (collection, ii); + if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node)) { + WebKitDOMDocument *content; + + content = webkit_dom_html_iframe_element_get_content_document (WEBKIT_DOM_HTML_IFRAME_ELEMENT (node)); + if (content) + web_extension_register_element_clicked_in_document (extension, page_id, content, element_class); + } + } + } + g_clear_object (&collection); +} + +static void +handle_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + guint64 page_id; + EWebExtension *extension = E_WEB_EXTENSION (user_data); + WebKitDOMDocument *document; + WebKitWebExtension *web_extension = extension->priv->wk_extension; + WebKitWebPage *web_page; + + if (g_strcmp0 (interface_name, E_WEB_EXTENSION_INTERFACE) != 0) + return; + + if (camel_debug ("wex")) + printf ("EWebExtension - %s - %s\n", G_STRFUNC, method_name); + if (g_strcmp0 (method_name, "RegisterElementClicked") == 0) { + const gchar *element_class = NULL; + + g_variant_get (parameters, "(t&s)", &page_id, &element_class); + + web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id); + if (!web_page) + return; + + if (!element_class || !*element_class) { + g_warn_if_fail (element_class && *element_class); + } else { + document = webkit_web_page_get_dom_document (web_page); + web_extension_register_element_clicked_in_document (extension, page_id, document, element_class); + } + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "SetElementHidden") == 0) { + const gchar *element_id = NULL; + gboolean hidden = FALSE; + + g_variant_get (parameters, "(t&sb)", &page_id, &element_id, &hidden); + + web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id); + if (!web_page) + return; + + if (!element_id || !*element_id) { + g_warn_if_fail (element_id && *element_id); + } else { + document = webkit_web_page_get_dom_document (web_page); + + /* A secret short-cut, to not have two functions for basically the same thing ("hide attachment" and "hide element") */ + if (!hidden && g_str_has_prefix (element_id, "attachment-wrapper-")) { + WebKitDOMElement *element; + + element = e_dom_utils_find_element_by_id (document, element_id); + + if (WEBKIT_DOM_IS_HTML_ELEMENT (element) && + webkit_dom_element_get_child_element_count (element) == 0) { + gchar *inner_html_data; + + inner_html_data = webkit_dom_element_get_attribute (element, "inner-html-data"); + if (inner_html_data && *inner_html_data) { + webkit_dom_element_set_inner_html (element, inner_html_data, NULL); + webkit_dom_element_remove_attribute (element, "inner-html-data"); + } + + g_free (inner_html_data); + } + } + + e_dom_utils_hide_element (document, element_id, hidden); + } + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "SetElementStyleProperty") == 0) { + const gchar *element_id = NULL, *property_name = NULL, *value = NULL, *priority = NULL; + + g_variant_get (parameters, "(t&s&s&s&s)", &page_id, &element_id, &property_name, &value, &priority); + + web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id); + if (!web_page) + return; + + if (!element_id || !*element_id || !property_name || !*property_name) { + g_warn_if_fail (element_id && *element_id); + g_warn_if_fail (property_name && *property_name); + } else { + WebKitDOMElement *element; + gboolean use_child = FALSE; + gchar *tmp = NULL; + + /* element_id can be also of the form: "id::child", where the change will + be done on the first child of it */ + use_child = g_str_has_suffix (element_id, "::child"); + if (use_child) { + tmp = g_strdup (element_id); + tmp[strlen (tmp) - 7] = '\0'; + + element_id = tmp; + } + + document = webkit_web_page_get_dom_document (web_page); + element = e_dom_utils_find_element_by_id (document, element_id); + + if (use_child && element) + element = webkit_dom_element_get_first_element_child (element); + + if (element) { + WebKitDOMCSSStyleDeclaration *css; + + css = webkit_dom_element_get_style (element); + + if (value && *value) + webkit_dom_css_style_declaration_set_property (css, property_name, value, priority, NULL); + else + g_free (webkit_dom_css_style_declaration_remove_property (css, property_name, NULL)); + + g_clear_object (&css); + } + + g_free (tmp); + } + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "SetElementAttribute") == 0) { + const gchar *element_id = NULL, *namespace_uri = NULL, *qualified_name = NULL, *value = NULL; + + g_variant_get (parameters, "(t&s&s&s&s)", &page_id, &element_id, &namespace_uri, &qualified_name, &value); + + web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id); + if (!web_page) + return; + + if (!element_id || !*element_id || !qualified_name || !*qualified_name) { + g_warn_if_fail (element_id && *element_id); + g_warn_if_fail (qualified_name && *qualified_name); + } else { + WebKitDOMElement *element; + gboolean use_child = FALSE; + gchar *tmp = NULL; + + /* element_id can be also of the form: "id::child", where the change will + be done on the first child of it */ + use_child = g_str_has_suffix (element_id, "::child"); + if (use_child) { + tmp = g_strdup (element_id); + tmp[strlen (tmp) - 7] = '\0'; + + element_id = tmp; + } + + if (namespace_uri && !*namespace_uri) + namespace_uri = NULL; + + document = webkit_web_page_get_dom_document (web_page); + element = e_dom_utils_find_element_by_id (document, element_id); + + if (use_child && element) + element = webkit_dom_element_get_first_element_child (element); + + if (element) { + if (value && *value) + webkit_dom_element_set_attribute_ns (element, namespace_uri, qualified_name, value, NULL); + else + webkit_dom_element_remove_attribute_ns (element, namespace_uri, qualified_name); + } + + g_free (tmp); + } + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "DocumentHasSelection") == 0) { + gboolean has_selection; + + g_variant_get (parameters, "(t)", &page_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + has_selection = e_dom_utils_document_has_selection (document); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(b)", has_selection)); + } else if (g_strcmp0 (method_name, "GetDocumentContentHTML") == 0) { + gchar *html_content; + + g_variant_get (parameters, "(t)", &page_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + html_content = e_dom_utils_get_document_content_html (document); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + html_content ? html_content : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "GetSelectionContentHTML") == 0) { + gchar *html_content; + + g_variant_get (parameters, "(t)", &page_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + html_content = e_dom_utils_get_selection_content_html (document); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + html_content ? html_content : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "GetSelectionContentText") == 0) { + gchar *text_content; + + g_variant_get (parameters, "(t)", &page_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + text_content = e_dom_utils_get_selection_content_text (document); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new_take_string (text_content)); + } else if (g_strcmp0 (method_name, "AddCSSRuleIntoStyleSheet") == 0) { + const gchar *style_sheet_id, *selector, *style; + + g_variant_get ( + parameters, + "(t&s&s&s)", + &page_id, &style_sheet_id, &selector, &style); + + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + e_dom_utils_add_css_rule_into_style_sheet (document, style_sheet_id, selector, style); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "CreateAndAddCSSStyleSheet") == 0) { + const gchar *style_sheet_id; + + g_variant_get (parameters, "(t&s)", &page_id, &style_sheet_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + e_dom_utils_create_and_add_css_style_sheet (document, style_sheet_id); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EABContactFormatterBindDOM") == 0) { + g_variant_get (parameters, "(t)", &page_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + e_dom_utils_eab_contact_formatter_bind_dom (document); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "EMailDisplayBindDOM") == 0) { + g_variant_get (parameters, "(t)", &page_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + e_dom_utils_e_mail_display_bind_dom (document, connection); + e_dom_utils_bind_focus_on_elements (document, connection); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "ElementExists") == 0) { + const gchar *element_id; + gboolean element_exists; + + g_variant_get (parameters, "(t&s)", &page_id, &element_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + element_exists = e_dom_utils_element_exists (document, element_id); + + g_dbus_method_invocation_return_value ( + invocation, g_variant_new ("(bt)", element_exists, page_id)); + } else if (g_strcmp0 (method_name, "GetActiveElementName") == 0) { + gchar *element_name; + + g_variant_get (parameters, "(t)", &page_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + element_name = e_dom_utils_get_active_element_name (document); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ( + "(@s)", + g_variant_new_take_string ( + element_name ? element_name : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "EMailPartHeadersBindDOMElement") == 0) { + const gchar *element_id; + + g_variant_get (parameters, "(t&s)", &page_id, &element_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + e_dom_utils_e_mail_part_headers_bind_dom_element (document, element_id); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "VCardInlineBindDOM") == 0) { + const gchar *element_id; + + g_variant_get (parameters, "(t&s)", &page_id, &element_id); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + e_dom_utils_module_vcard_inline_bind_dom ( + document, element_id, connection); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "VCardInlineUpdateButton") == 0) { + const gchar *button_id, *html_label, *access_key; + + g_variant_get ( + parameters, + "(t&s&s&s)", + &page_id, &button_id, &html_label, &access_key); + + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + e_dom_utils_module_vcard_inline_update_button ( + document, button_id, html_label, access_key); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "VCardInlineSetIFrameSrc") == 0) { + const gchar *src, *button_id; + + g_variant_get (parameters, "(t&s&s)", &page_id, &button_id, &src); + web_page = get_webkit_web_page_or_return_dbus_error ( + invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + e_dom_utils_module_vcard_inline_set_iframe_src (document, button_id, src); + + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "GetDocumentURIFromPoint") == 0) { + WebKitDOMDocument *document_at_point; + gchar *document_uri = NULL; + gint32 xx = 0, yy = 0; + + g_variant_get (parameters, "(tii)", &page_id, &xx, &yy); + web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + document_at_point = e_dom_utils_get_document_from_point (document, xx, yy); + + if (document_at_point) + document_uri = webkit_dom_document_get_document_uri (document_at_point); + + g_dbus_method_invocation_return_value ( + invocation, + g_variant_new ("(@s)", g_variant_new_take_string (document_uri ? document_uri : g_strdup ("")))); + } else if (g_strcmp0 (method_name, "SetDocumentIFrameSrc") == 0) { + const gchar *document_uri = NULL, *new_iframe_src = NULL; + WebKitDOMDocument *iframe_document; + + g_variant_get (parameters, "(t&s&s)", &page_id, &document_uri, &new_iframe_src); + web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id); + if (!web_page) + return; + + document = webkit_web_page_get_dom_document (web_page); + iframe_document = e_dom_utils_find_document_with_uri (document, document_uri); + + if (iframe_document) { + WebKitDOMDOMWindow *dom_window; + WebKitDOMElement *frame_element; + + /* Get frame's window and from the window the actual <iframe> element */ + dom_window = webkit_dom_document_get_default_view (iframe_document); + frame_element = webkit_dom_dom_window_get_frame_element (dom_window); + webkit_dom_html_iframe_element_set_src ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame_element), new_iframe_src); + g_clear_object (&dom_window); + } + + g_dbus_method_invocation_return_value (invocation, NULL); + } +} + +static GVariant * +handle_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + EWebExtension *extension = E_WEB_EXTENSION (user_data); + GVariant *variant = NULL; + + if (g_strcmp0 (property_name, "NeedInput") == 0) { + variant = g_variant_new_boolean (extension->priv->need_input); + } + + return variant; +} + +static gboolean +handle_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + EWebExtension *extension = E_WEB_EXTENSION (user_data); + GError *local_error = NULL; + GVariantBuilder *builder; + + builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + + if (g_strcmp0 (property_name, "NeedInput") == 0) { + gboolean value = g_variant_get_boolean (variant); + + if (value == extension->priv->need_input) + goto exit; + + extension->priv->need_input = value; + + g_variant_builder_add (builder, + "{sv}", + "NeedInput", + g_variant_new_boolean (value)); + } + + g_dbus_connection_emit_signal (connection, + NULL, + object_path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + g_variant_new ( + "(sa{sv}as)", + interface_name, + builder, + NULL), + &local_error); + + g_assert_no_error (local_error); + + exit: + g_variant_builder_unref (builder); + + return TRUE; +} + +static const GDBusInterfaceVTable interface_vtable = { + handle_method_call, + handle_get_property, + handle_set_property +}; + +static void +e_web_extension_dispose (GObject *object) +{ + EWebExtension *extension = E_WEB_EXTENSION (object); + + if (extension->priv->dbus_connection) { + g_dbus_connection_unregister_object ( + extension->priv->dbus_connection, + extension->priv->registration_id); + extension->priv->registration_id = 0; + extension->priv->dbus_connection = NULL; + } + + g_clear_object (&extension->priv->wk_extension); + + G_OBJECT_CLASS (e_web_extension_parent_class)->dispose (object); +} + +static void +e_web_extension_class_init (EWebExtensionClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = e_web_extension_dispose; + + g_type_class_add_private (object_class, sizeof(EWebExtensionPrivate)); +} + +static void +e_web_extension_init (EWebExtension *extension) +{ + extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension, E_TYPE_WEB_EXTENSION, EWebExtensionPrivate); + + extension->priv->initialized = FALSE; + extension->priv->need_input = FALSE; +} + +static gpointer +e_web_extension_create_instance(gpointer data) +{ + return g_object_new (E_TYPE_WEB_EXTENSION, NULL); +} + +EWebExtension * +e_web_extension_get (void) +{ + static GOnce once_init = G_ONCE_INIT; + return E_WEB_EXTENSION (g_once (&once_init, e_web_extension_create_instance, NULL)); +} + +static gboolean +web_page_send_request_cb (WebKitWebPage *web_page, + WebKitURIRequest *request, + WebKitURIResponse *redirected_response, + EWebExtension *extension) +{ + const gchar *request_uri; + const gchar *page_uri; + + request_uri = webkit_uri_request_get_uri (request); + page_uri = webkit_web_page_get_uri (web_page); + + /* Always load the main resource. */ + if (g_strcmp0 (request_uri, page_uri) == 0 || + /* Do not influence real pages, like those with eds OAuth sign-in */ + g_str_has_prefix (page_uri, "http:") || + g_str_has_prefix (page_uri, "https:")) + return FALSE; + + if (g_str_has_prefix (request_uri, "http:") || + g_str_has_prefix (request_uri, "https:")) { + gchar *new_uri; + + new_uri = g_strconcat ("evo-", request_uri, NULL); + + webkit_uri_request_set_uri (request, new_uri); + + g_free (new_uri); + } + + return FALSE; +} + +static void +web_page_document_loaded_cb (WebKitWebPage *web_page, + gpointer user_data) +{ + WebKitDOMDocument *document; + + document = webkit_web_page_get_dom_document (web_page); + + e_dom_utils_replace_local_image_links (document); + + if ((webkit_dom_document_query_selector ( + document, "[data-evo-signature-plain-text-mode]", NULL))) { + + WebKitDOMHTMLElement *body; + + body = webkit_dom_document_get_body (document); + + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body), + "style", + "font-family: Monospace;", + NULL); + } +} + +static void +web_page_created_cb (WebKitWebExtension *wk_extension, + WebKitWebPage *web_page, + EWebExtension *extension) +{ + g_signal_connect_object ( + web_page, "send-request", + G_CALLBACK (web_page_send_request_cb), + extension, 0); + + g_signal_connect_object ( + web_page, "document-loaded", + G_CALLBACK (web_page_document_loaded_cb), + extension, 0); + +} + +void +e_web_extension_initialize (EWebExtension *extension, + WebKitWebExtension *wk_extension) +{ + g_return_if_fail (E_IS_WEB_EXTENSION (extension)); + + if (extension->priv->initialized) + return; + + extension->priv->initialized = TRUE; + + extension->priv->wk_extension = g_object_ref (wk_extension); + + g_signal_connect ( + wk_extension, "page-created", + G_CALLBACK (web_page_created_cb), extension); +} + +void +e_web_extension_dbus_register (EWebExtension *extension, + GDBusConnection *connection) +{ + GError *error = NULL; + static GDBusNodeInfo *introspection_data = NULL; + + g_return_if_fail (E_IS_WEB_EXTENSION (extension)); + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + + if (!introspection_data) { + introspection_data = + g_dbus_node_info_new_for_xml (introspection_xml, NULL); + + extension->priv->registration_id = + g_dbus_connection_register_object ( + connection, + E_WEB_EXTENSION_OBJECT_PATH, + introspection_data->interfaces[0], + &interface_vtable, + extension, + NULL, + &error); + + if (!extension->priv->registration_id) { + g_warning ("Failed to register object: %s\n", error->message); + g_error_free (error); + } else { + extension->priv->dbus_connection = connection; + g_object_add_weak_pointer ( + G_OBJECT (connection), + (gpointer *)&extension->priv->dbus_connection); + } + } +} diff --git a/web-extensions/e-web-extension.h b/web-extensions/e-web-extension.h new file mode 100644 index 0000000..a1f5837 --- /dev/null +++ b/web-extensions/e-web-extension.h @@ -0,0 +1,72 @@ +/* + * e-web-extension.h + * + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_WEB_EXTENSION_H +#define E_WEB_EXTENSION_H + +#include <webkit2/webkit-web-extension.h> +#include <glib-object.h> + +/* Standard GObject macros */ +#define E_TYPE_WEB_EXTENSION \ + (e_web_extension_get_type ()) +#define E_WEB_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_WEB_EXTENSION, EWebExtension)) +#define E_WEB_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_WEB_EXTENSION, EWebExtensionClass)) +#define E_IS_WEB_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_WEB_EXTENSION)) +#define E_IS_WEB_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_WEB_EXTENSION)) +#define E_WEB_EXTENSION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_WEB_EXTENSION, EWebExtensionClass)) + +G_BEGIN_DECLS + +typedef struct _EWebExtension EWebExtension; +typedef struct _EWebExtensionClass EWebExtensionClass; +typedef struct _EWebExtensionPrivate EWebExtensionPrivate; + +struct _EWebExtension { + GObject parent; + EWebExtensionPrivate *priv; +}; + +struct _EWebExtensionClass +{ + GObjectClass parent_class; +}; + +GType e_web_extension_get_type (void) G_GNUC_CONST; + +EWebExtension * e_web_extension_get (void); + +void e_web_extension_initialize (EWebExtension *extension, + WebKitWebExtension *wk_extension); + +void e_web_extension_dbus_register (EWebExtension *extension, + GDBusConnection *connection); + +G_END_DECLS + +#endif /* E_WEB_EXTENSION_H */ |